capture
[rrq/gorite.git] / com / intendico / data / Snapshot.java
1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
3
4 This file is part of GORITE.
5
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
19
20 package com.intendico.data;
21
22 import java.util.Vector;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.Observer;
26
27 /**
28  * The Snapshot class implements a query overlay for capturing changes
29  * to a source {@link Query} in respect of the added and lost valid
30  * binding combinations for the {@link Ref} objects
31  * involved. Temporally a snapshot is taken by the {@link #reset}
32  * method, as well as at construction. Thereafter successive {@link
33  * #next} invocations provide the added valid binding combinations
34  * since the prior snapshot.
35  *
36  * <p> Following a reset, the Snapshot also holds the lost bindings,
37  * which can be inspected separately.
38  *
39  * <p> A Snapshot may hold a non-null {@link #sync} object, which is
40  * used for synchronizing on during {@link #reset}. Any concurrent
41  * thread that updates queried components should synchronize on the
42  * same object, and in that way avoid multi-threading conflict.
43  */
44 public class Snapshot implements Query {
45
46     /**
47      * Holds all the bindings found at the most recent snapshot.
48      */
49     public HashSet/*<Vector>*/ snapshot;
50
51     /**
52      * Holds the bindings that were valid before the most recent
53      * snapshot, but were no longer valid at that snapshot.
54      */
55     public HashSet/*<Vector>*/ lost = new HashSet/*<Vector>*/();
56
57     /**
58      * Holds the Ref objects in a Vector.
59      */
60     public Vector/*<Ref>*/ refs;
61
62     /**
63      * The iterator for recalling successive new bindings by the
64      * {@link #next} method.
65      */
66     public Iterator/*<Vector>*/ bindings;
67
68     /**
69      * The query being monitored.
70      */
71     public Query query;
72
73     /**
74      * The object, if any, to synchronize on during {@link
75      * #reset}. This is to support multi-thread access to queried
76      * components; any concurrent thread needs to synchronize on the
77      * same object.
78      */
79     public Object sync = null;
80
81     /**
82      * Constructor.
83      */
84     public Snapshot(Query q) {
85         query = q;
86         refs = query.getRefs( new Vector/*<Ref>*/() );
87         try {
88             reset();
89         } catch (Exception e) {
90             e.printStackTrace();
91         }
92     }
93
94     /**
95      * Alternative constructor with a synchronization object.
96      */
97     public Snapshot(Object lock,Query q) {
98         this( q );
99         sync = lock;
100     }
101
102     /**
103      * The {@link Query#copy} method implemented by creating a new
104      * Snapshot with a copy of the monitored {@link #query}, and
105      * sharing any {@link #sync} object.
106      */
107     public Query copy(Vector/*<Ref>*/ newrefs) throws Exception {
108         return new Snapshot( sync, query.copy( newrefs ) );
109     }
110
111     /**
112      * The {@link Query#reset} method implemented by capturing a new
113      * snapshot of the given query, and initialising the Iterator for
114      * the collection of bindings that are new since the last
115      * snapshot.
116      *
117      * <p> This method updates {@link #snapshot}, {@link #lost} and
118      * {@link #bindings}.
119      */
120     public void reset() throws Exception {
121         if ( sync == null ) {
122             doReset();
123         } else {
124             synchronized( sync ) {
125                 doReset();
126             }
127         }
128     }
129
130     /**
131      * Actual reset method, which may and might not be synchronized.
132      */
133     private void doReset() throws Exception {
134         lost = snapshot != null? snapshot : new HashSet/*<Vector>*/();
135         HashSet/*<Vector>*/ is = new HashSet/*<Vector>*/();
136         snapshot = new HashSet/*<Vector>*/();
137         Ref.clear( refs );
138         query.reset();
139         boolean none = true;
140         while ( query.next() ) {
141             none = false;
142             Vector v = Ref.getBinding( refs );
143             if ( snapshot.contains( v ) )
144                 continue;
145             snapshot.add( v );
146             if ( lost != null && lost.contains( v ) ) {
147                 lost.remove( v );
148             } else {
149                 is.add( v );
150             } 
151         }
152         bindings = none? null : is.iterator();
153     }
154
155     /**
156      * Produces the succession of bindings that the qurey was valid
157      * for at the last snapshot, but not at the one prior.
158      *
159      * <p> This method uses {@link #bindings}.
160      */
161     public boolean next() {
162         if ( bindings == null )
163             return false;
164         if ( bindings.hasNext() ) {
165             Ref.bind( refs, (Vector) bindings.next() );
166         } else {
167             bindings = null;
168             return false;
169         }
170         if ( ! bindings.hasNext() )
171             bindings = null;
172         return true;
173     }
174
175     /**
176      * The {@link Query#getRefs} method implemented by returning the
177      * query's Ref object.
178      */
179     public Vector/*<Ref>*/ getRefs(Vector/*<Ref>*/ v) {
180         return query.getRefs( v );
181     }
182
183     /**
184      * The {@link Query#addObserver} method implemented by adding
185      * the observer to the query.
186      */
187     public void addObserver(Observer x) {
188         query.addObserver( x );
189     }
190
191     /**
192      * The {@link Query#deleteObserver} method implemented by
193      * removing the observer from the query.
194      */
195     public void deleteObserver(Observer x) {
196         query.deleteObserver( x );
197     }
198
199     /**
200      * The {@link Query#addable} method implemented by forwarding to
201      * the contained query.
202      */
203     public boolean addable() {
204         return query.addable();
205     }
206
207     /**
208      * The {@link Query#add} method implemented by forwarding to the
209      * contained query.
210      */
211     public boolean add() {
212         return query.add();
213     }
214
215     /**
216      * The {@link Query#removable} method implemented by forwarding to
217      * the wrapped query.
218      */
219     public boolean removable() {
220         return query.removable();
221     }
222
223     /**
224      * The {@link Query#remove} method implemented by forwarding to
225      * the wrapped query.
226      */
227     public boolean remove() {
228         return query.remove();
229     }
230
231     /**
232      * Utility method to remove all remaining bindings for the
233      * query. This method applies to the current snapshot, and might
234      * make most sense for a newly created Snapshot object, where it
235      * will remove all matching bindings.
236      */
237     public void removeAll() {
238         while ( next() ) {
239             remove();
240         }
241     }
242
243     /**
244      * Returns a String representation of the Snapshot object.
245      */
246     public String toString() {
247         return "Snapshot( " + query.toString() + " )";
248     }
249 }