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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
14 * @author Toni Kalajainen
\r
16 package org.simantics.utils.datastructures.context;
\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
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
37 * TODO Optimize getItemsByClass queries (Create optimizing structures after 1st query)
\r
38 * TODO Optimize getSingleItem query
\r
42 public class Context<E> extends AbstractDisposable implements IContext<E> {
\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
49 final Class<E> clazz;
\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
55 public Context(Class<E> clazz) {
\r
57 snapshotArray = createArray(clazz, 0);
\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
69 snapshotArray = createSnapshot(set);
\r
71 executables = listeners.getExecutables(itemAdded, this, item);
\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
80 ThreadUtils.multiSyncExec(executables);
\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
90 snapshotArray = createSnapshot(set);
\r
91 executables = listeners.getExecutables(itemRemoved, this, item);
\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
100 ThreadUtils.multiSyncExec(executables);
\r
105 public synchronized boolean contains(E item) {
\r
106 assertNotDisposed();
\r
107 return set.contains(item);
\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
119 for (E item : snapshotArray)
\r
120 listeners.addExecutables(executables, itemRemoved, this, item);
\r
123 this.snapshotArray = createSnapshot(set);
\r
125 ThreadUtils.multiSyncExec(executables);
\r
128 @SuppressWarnings("unchecked")
\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
135 result = new ArrayList<R>();
\r
137 if (clazz.isAssignableFrom(i.getClass()))
\r
139 classQueryResultCache.put(clazz, result);
\r
143 @SuppressWarnings("unchecked")
\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
150 if (clazz.isAssignableFrom(i.getClass()))
\r
155 @SuppressWarnings({ "unchecked" })
\r
156 private <R extends E> R[] createArray(Class<R> clazz, int length)
\r
158 return (R[]) Array.newInstance(clazz, length);
\r
161 E[] createSnapshot(Set<E> set) {
\r
162 E[] result = createArray(clazz, set.size());
\r
165 result[index++] = i;
\r
169 public synchronized <R extends E> R getSingleItem(Class<R> clazz)
\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
179 public E[] toArray() {
\r
180 assertNotDisposed();
\r
181 return snapshotArray;
\r
185 public void addContextListener(IContextListener<E> listener) {
\r
186 assertNotDisposed();
\r
187 listeners.add(listener);
\r
191 public void removeContextListener(IContextListener<E> listener) {
\r
192 assertNotDisposed();
\r
193 listeners.remove(listener);
\r
197 public void addContextListener(IThreadWorkQueue thread,
\r
198 IContextListener<E> listener) {
\r
199 assertNotDisposed();
\r
200 listeners.add(thread, listener);
\r
204 public void removeContextListener(IThreadWorkQueue thread,
\r
205 IContextListener<E> listener) {
\r
206 assertNotDisposed();
\r
207 listeners.remove(thread, listener);
\r
210 private static Method itemAdded = SyncListenerList.getMethod(IContextListener.class, "itemAdded");
\r
211 private static Method itemRemoved = SyncListenerList.getMethod(IContextListener.class, "itemRemoved");
\r
213 // private void fireItemAdded(E item)
\r
215 // listeners.fireEventSync(itemAdded, this, item);
\r
218 // private void fireItemRemoved(E item)
\r
220 // listeners.fireEventSync(itemRemoved, this, item);
\r
223 @SuppressWarnings("unchecked")
\r
225 public <R extends E> R getAtMostOneItemOfClass(Class<R> clazz) {
\r
226 assertNotDisposed();
\r
230 if (clazz.isAssignableFrom(i.getClass())) {
\r
235 if (count==0) return null;
\r
237 throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+count);
\r
242 protected void doDispose() {
\r
246 public String toString() {
\r
247 String s = EString.implode(snapshotArray, "\n");
\r
248 return s != null ? s : "";
\r