]> gerrit.simantics Code Review - simantics/platform.git/blob
7b5465992cbcf4957f19664fea68b79c0cab1e71
[simantics/platform.git] /
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 /*
13  *
14  * @author Toni Kalajainen
15  */
16 package org.simantics.utils.datastructures.context;
17
18 import java.lang.reflect.Array;
19 import java.lang.reflect.Method;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.simantics.utils.datastructures.disposable.AbstractDisposable;
29 import org.simantics.utils.strings.EString;
30 import org.simantics.utils.threads.Executable;
31 import org.simantics.utils.threads.IThreadWorkQueue;
32 import org.simantics.utils.threads.SyncListenerList;
33 import org.simantics.utils.threads.ThreadUtils;
34
35 /**
36  * 
37  * TODO Optimize getItemsByClass queries (Create optimizing structures after 1st query)
38  * TODO Optimize getSingleItem query
39  *
40  * @param <E>
41  */
42 public class Context<E> extends AbstractDisposable implements IContext<E> {
43
44         protected Set<E> set = new HashSet<E>();
45         @SuppressWarnings({ "rawtypes" })
46     protected SyncListenerList<IContextListener> listeners =
47                 new SyncListenerList<IContextListener>(IContextListener.class);
48         
49         final Class<E> clazz;
50         
51         private E[] snapshotArray;
52         private Map<Class<? extends E>, Collection<? extends E>> classQueryResultCache = 
53                 new HashMap<Class<? extends E>, Collection<? extends E>>();
54         
55         public Context(Class<E> clazz) {
56                 this.clazz = clazz;
57                 snapshotArray = createArray(clazz, 0);
58         }
59         
60         @Override
61         public void add(E item) {
62             assertNotDisposed();
63                 Executable executables[];
64                 synchronized(this) {
65                         if (set.contains(item))
66                                 throw new IllegalArgumentException("Context already contains item "+item);
67                 
68                         set.add(item);
69                         snapshotArray = createSnapshot(set);
70                         
71                         executables = listeners.getExecutables(itemAdded, this, item);
72                         
73                         Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();
74                         while (i.hasNext()) {
75                                 Class<? extends E> c = i.next();
76                                 if (c.isInstance(item))
77                                         i.remove();                             
78                         }
79                 }
80                 ThreadUtils.multiSyncExec(executables);
81         }
82
83         @Override
84         public boolean remove(E item) {
85         assertNotDisposed();
86                 Executable executables[];               
87                 synchronized(this) {
88                         if (!set.remove(item))                  
89                                 return false;
90                         snapshotArray = createSnapshot(set);
91                         executables = listeners.getExecutables(itemRemoved, this, item);
92                         
93                         Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();
94                         while (i.hasNext()) {
95                                 Class<? extends E> c = i.next();
96                                 if (c.isInstance(item))
97                                         i.remove();                             
98                         }                       
99                 }
100                 ThreadUtils.multiSyncExec(executables);
101                 return true;
102         }
103         
104         @Override
105         public synchronized boolean contains(E item) {
106         assertNotDisposed();
107                 return set.contains(item);
108         }
109
110     @Override
111     public void clear() {
112         assertNotDisposed();
113         classQueryResultCache.clear();
114         ArrayList<Executable> executables = new ArrayList<Executable>();
115         synchronized(this) {
116                 if (set.isEmpty())
117                         return;
118
119                 for (E item : snapshotArray)
120                         listeners.addExecutables(executables, itemRemoved, this, item);
121                 
122                 set.clear();
123                 this.snapshotArray = createSnapshot(set);
124         }
125         ThreadUtils.multiSyncExec(executables);
126     }
127
128         @SuppressWarnings("unchecked")
129     @Override
130         public synchronized <R extends E> Collection<R> getItemsByClass(Class<R> clazz) {
131         assertNotDisposed();        
132                 Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);
133                 if (result!=null)
134                         return result;
135                 result = new ArrayList<R>();
136                 for (E i : set)
137                         if (clazz.isAssignableFrom(i.getClass()))
138                                 result.add((R)i);
139                 classQueryResultCache.put(clazz, result);
140                 return result;
141         }
142
143         @SuppressWarnings("unchecked")
144         @Override
145         public synchronized <R> boolean containsItemByClass(Class<R> clazz) {
146         assertNotDisposed();
147                 Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);
148                 if (result!=null) return !result.isEmpty();
149                 for (E i : set)
150                         if (clazz.isAssignableFrom(i.getClass()))
151                                 return true;
152                 return false;
153         }
154         
155     @SuppressWarnings({ "unchecked" })
156     private <R extends E> R[] createArray(Class<R> clazz, int length)
157     {
158         return (R[]) Array.newInstance(clazz, length);
159     }    
160         
161         E[] createSnapshot(Set<E> set) {
162                 E[] result = createArray(clazz, set.size());
163                 int index = 0;
164                 for (E i : set)
165                         result[index++] = i; 
166                 return result;
167         }       
168         
169         public synchronized <R extends E> R getSingleItem(Class<R> clazz)
170         {
171         assertNotDisposed();
172                 Collection<R> result = getItemsByClass(clazz);
173                 if (result.size()==1)
174                         return result.iterator().next();
175                 throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+result.size());                  
176         }
177
178         @Override
179         public E[] toArray() {
180         assertNotDisposed();
181                 return snapshotArray;
182         }
183         
184         @Override
185         public void addContextListener(IContextListener<E> listener) {
186         assertNotDisposed();
187                 listeners.add(listener);
188         }
189
190         @Override
191         public void removeContextListener(IContextListener<E> listener) {
192         assertNotDisposed();
193                 listeners.remove(listener);
194         }       
195         
196         @Override
197         public void addContextListener(IThreadWorkQueue thread,
198                         IContextListener<E> listener) {
199         assertNotDisposed();
200                 listeners.add(thread, listener);
201         }
202
203         @Override
204         public void removeContextListener(IThreadWorkQueue thread,
205                         IContextListener<E> listener) {
206         assertNotDisposed();
207                 listeners.remove(thread, listener);
208         }               
209
210         private static Method itemAdded = SyncListenerList.getMethod(IContextListener.class, "itemAdded");
211         private static Method itemRemoved = SyncListenerList.getMethod(IContextListener.class, "itemRemoved");
212
213 //      private void fireItemAdded(E item)
214 //      {
215 //              listeners.fireEventSync(itemAdded, this, item);
216 //      }
217 //      
218 //      private void fireItemRemoved(E item)
219 //      {
220 //              listeners.fireEventSync(itemRemoved, this, item);
221 //      }
222
223         @SuppressWarnings("unchecked")
224     @Override
225         public <R extends E> R getAtMostOneItemOfClass(Class<R> clazz) {
226         assertNotDisposed();
227             int count = 0;
228             R r = null;
229         for (E i : set) {
230             if (clazz.isAssignableFrom(i.getClass())) {
231                 ++count;
232                 r = (R) i;
233             }
234         }
235         if (count==0) return null;
236         if (count>1)
237                         throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+count);
238                 return r;
239         }
240         
241         @Override
242         protected void doDispose() {
243         }
244
245         @Override
246         public String toString() {
247             String s = EString.implode(snapshotArray, "\n");
248             return s != null ? s : "";
249         }
250
251 }