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