]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.common/src/org/simantics/db/common/utils/GraphList.java
GraphList: List interface to linked lists in Simantics database
[simantics/platform.git] / bundles / org.simantics.db.common / src / org / simantics / db / common / utils / GraphList.java
1 package org.simantics.db.common.utils;
2
3 import java.util.AbstractSequentialList;
4 import java.util.ListIterator;
5 import java.util.NoSuchElementException;
6
7 import org.simantics.db.ReadGraph;
8 import org.simantics.db.Resource;
9 import org.simantics.db.WriteGraph;
10 import org.simantics.db.exception.DatabaseException;
11 import org.simantics.layer0.Layer0;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14
15 /**
16  * Java List interface to linked lists in Simantics DB.
17  */
18 public class GraphList extends AbstractSequentialList<Resource> {
19     
20     private final static Logger LOGGER = LoggerFactory.getLogger(GraphList.class);
21     
22     private final ReadGraph graph;
23     private final Layer0 L0;
24     private final Resource root;
25     private final Resource elementRelation;
26     
27     public GraphList(ReadGraph graph, Resource root, Resource elementRelation) {
28         this.graph = graph;
29         this.L0 = Layer0.getInstance(graph);
30         this.root = root;
31         this.elementRelation = elementRelation;
32     }
33     
34     public GraphList(ReadGraph graph, Resource root) throws DatabaseException {
35         this.graph = graph;
36         this.L0 = Layer0.getInstance(graph);
37         this.root = root;
38         this.elementRelation = graph.getSingleObject(root, L0.List_ElementPredicate);
39     }
40     
41     class GraphListIterator implements ListIterator<Resource> {
42         private Resource prev;
43         private Resource next;
44         private int id;
45         private int lastOp;
46         
47         public GraphListIterator(Resource prev, Resource next, int id) {
48             LOGGER.info("GraphListIterator root="+root+" prev=" + prev + " next=" + next);
49             this.prev = prev;
50             this.next = next;
51             this.id = id;
52             this.lastOp = 0;
53         }
54
55         @Override
56         public boolean hasNext() {
57             LOGGER.info("hasNext root="+root+" prev=" + prev + " next=" + next);
58             return next != root;
59         }
60
61         void goNext() {
62             LOGGER.info("goNext prev=" + prev + " next=" + next);
63             if(next == root) {
64                 lastOp = 0;
65                 throw new NoSuchElementException();
66             }
67             prev = next;
68             next = browseNext(next);
69             ++id;
70             lastOp = 1;
71             LOGGER.info("       prev=" + prev + " next=" + next);
72         }
73         
74         void goPrev() {
75             if(prev == root) {
76                 lastOp = 0;
77                 throw new NoSuchElementException();
78             }
79             next = prev;
80             prev = browsePrev(prev);
81             --id;
82             lastOp = -1;
83         }
84         
85         @Override
86         public Resource next() {
87             goNext();
88             return browseElement(prev);
89         }
90
91         @Override
92         public boolean hasPrevious() {
93             return prev != root;
94         }
95
96         @Override
97         public Resource previous() {
98             goPrev();
99             return browseElement(next);
100         }
101
102         @Override
103         public int nextIndex() {
104             return id;
105         }
106
107         @Override
108         public int previousIndex() {
109             return id-1;
110         }
111
112         @Override
113         public void remove() {
114             Resource target;
115             if(lastOp == 1) {
116                 target = prev;
117                 prev = browsePrev(prev);
118             }
119             else if(lastOp == -1) {
120                 target = next;
121                 next = browseNext(next);
122             }
123             else
124                 throw new UnsupportedOperationException("Cannot remove element, when no previous next/prev call.");
125             doRemove(prev, target, next);
126             lastOp = 0;
127         }
128
129         @Override
130         public void set(Resource e) {
131             Resource target;
132             if(lastOp == 1)
133                 target = prev;
134             else if(lastOp == -1)
135                 target = next;
136             else
137                 throw new UnsupportedOperationException("Cannot remove element, when no previous next/prev call.");
138             doSet(target, e);
139         }
140
141         @Override
142         public void add(Resource e) {
143             addBetween(prev, next, e);
144             lastOp = 0;
145         }
146     }
147
148     @Override
149     public ListIterator<Resource> listIterator(int index) {
150         GraphListIterator it = new GraphListIterator(root, browseNext(root), 0);
151         while(index > 0) {
152             it.goNext();
153             --index;
154         }
155         return it;
156     }
157
158     private Resource browseNext(Resource cur) {
159         try {
160             return graph.getSingleObject(cur, L0.List_Next);
161         } catch(DatabaseException e) {
162             throw new RuntimeException(e);
163         }
164     }
165     
166     private Resource browsePrev(Resource cur) {
167         try {
168             return graph.getSingleObject(cur, L0.List_Previous);
169         } catch(DatabaseException e) {
170             throw new RuntimeException(e);
171         }
172     }
173     
174     private Resource browseElement(Resource cur) {
175         try {
176             LOGGER.info("browseElement " + cur);
177             return graph.getSingleObject(cur, elementRelation);
178         } catch(DatabaseException e) {
179             throw new RuntimeException(e);
180         }
181     }
182
183     private void addBetween(Resource prev, Resource next, Resource element) {
184         WriteGraph graph;
185         try {
186             graph = (WriteGraph)this.graph;
187         } catch(ClassCastException e) {
188             throw new UnsupportedOperationException("Cannot add element in read transaction.");            
189         }
190         try {
191             graph.deny(prev, L0.List_Next, L0.List_Previous, next);
192             
193             Resource newEntry = graph.newResource();
194             graph.claim(newEntry, L0.InstanceOf, L0.List_Entry);
195             graph.claim(newEntry, elementRelation, element);
196             graph.claim(newEntry, L0.IsOwnedBy, L0.IsComposedOf, root);
197             graph.claim(prev, L0.List_Next, L0.List_Previous, newEntry);
198             graph.claim(newEntry, L0.List_Next, L0.List_Previous, next);
199         } catch(DatabaseException e) {
200             throw new RuntimeException(e);
201         }
202     }
203
204     private void doSet(Resource target, Resource resource) {
205         WriteGraph graph;
206         try {
207             graph = (WriteGraph)this.graph;
208         } catch(ClassCastException e) {
209             throw new UnsupportedOperationException("Cannot set element in read transaction.");            
210         }
211         try {
212             graph.deny(target, elementRelation);
213             graph.claim(target, elementRelation, resource);
214         } catch(DatabaseException e) {
215             throw new RuntimeException(e);
216         }
217     }
218
219     private void doRemove(Resource prev, Resource target, Resource next) {
220         WriteGraph graph;
221         try {
222             graph = (WriteGraph)this.graph;
223         } catch(ClassCastException e) {
224             throw new UnsupportedOperationException("Cannot remove element in read transaction.");            
225         }
226         try {
227             graph.deny(target);
228             graph.claim(prev, L0.List_Next, L0.List_Previous, next);
229         } catch(DatabaseException e) {
230             throw new RuntimeException(e);
231         }
232     }
233
234     @Override
235     public int size() {
236         int count = 0;
237         for(Resource cur=browseNext(root);cur!=root;cur=browseNext(cur))
238             ++count;
239         return count;
240     }
241
242     @Override
243     public boolean isEmpty() {
244         try {
245             return graph.hasStatement(root, L0.List_Next, root);
246         } catch (DatabaseException e) {
247             throw new RuntimeException(e);
248         }
249     }
250 }