1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
14 * @author Toni Kalajainen
16 package org.simantics.utils.datastructures.context;
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;
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;
37 * TODO Optimize getItemsByClass queries (Create optimizing structures after 1st query)
38 * TODO Optimize getSingleItem query
42 public class Context<E> extends AbstractDisposable implements IContext<E> {
44 protected Set<E> set = new HashSet<E>();
45 @SuppressWarnings({ "rawtypes" })
46 protected SyncListenerList<IContextListener> listeners =
47 new SyncListenerList<IContextListener>(IContextListener.class);
51 private E[] snapshotArray;
52 private Map<Class<? extends E>, Collection<? extends E>> classQueryResultCache =
53 new HashMap<Class<? extends E>, Collection<? extends E>>();
55 public Context(Class<E> clazz) {
57 snapshotArray = createArray(clazz, 0);
61 public void add(E item) {
63 Executable executables[];
65 if (set.contains(item))
66 throw new IllegalArgumentException("Context already contains item "+item);
69 snapshotArray = createSnapshot(set);
71 executables = listeners.getExecutables(itemAdded, this, item);
73 Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();
75 Class<? extends E> c = i.next();
76 if (c.isInstance(item))
80 ThreadUtils.multiSyncExec(executables);
84 public boolean remove(E item) {
86 Executable executables[];
88 if (!set.remove(item))
90 snapshotArray = createSnapshot(set);
91 executables = listeners.getExecutables(itemRemoved, this, item);
93 Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();
95 Class<? extends E> c = i.next();
96 if (c.isInstance(item))
100 ThreadUtils.multiSyncExec(executables);
105 public synchronized boolean contains(E item) {
107 return set.contains(item);
111 public void clear() {
113 classQueryResultCache.clear();
114 ArrayList<Executable> executables = new ArrayList<Executable>();
119 for (E item : snapshotArray)
120 listeners.addExecutables(executables, itemRemoved, this, item);
123 this.snapshotArray = createSnapshot(set);
125 ThreadUtils.multiSyncExec(executables);
128 @SuppressWarnings("unchecked")
130 public synchronized <R extends E> Collection<R> getItemsByClass(Class<R> clazz) {
132 Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);
135 result = new ArrayList<R>();
137 if (clazz.isAssignableFrom(i.getClass()))
139 classQueryResultCache.put(clazz, result);
143 @SuppressWarnings("unchecked")
145 public synchronized <R> boolean containsItemByClass(Class<R> clazz) {
147 Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);
148 if (result!=null) return !result.isEmpty();
150 if (clazz.isAssignableFrom(i.getClass()))
155 @SuppressWarnings({ "unchecked" })
156 private <R extends E> R[] createArray(Class<R> clazz, int length)
158 return (R[]) Array.newInstance(clazz, length);
161 E[] createSnapshot(Set<E> set) {
162 E[] result = createArray(clazz, set.size());
169 public synchronized <R extends E> R getSingleItem(Class<R> clazz)
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());
179 public E[] toArray() {
181 return snapshotArray;
185 public void addContextListener(IContextListener<E> listener) {
187 listeners.add(listener);
191 public void removeContextListener(IContextListener<E> listener) {
193 listeners.remove(listener);
197 public void addContextListener(IThreadWorkQueue thread,
198 IContextListener<E> listener) {
200 listeners.add(thread, listener);
204 public void removeContextListener(IThreadWorkQueue thread,
205 IContextListener<E> listener) {
207 listeners.remove(thread, listener);
210 private static Method itemAdded = SyncListenerList.getMethod(IContextListener.class, "itemAdded");
211 private static Method itemRemoved = SyncListenerList.getMethod(IContextListener.class, "itemRemoved");
213 // private void fireItemAdded(E item)
215 // listeners.fireEventSync(itemAdded, this, item);
218 // private void fireItemRemoved(E item)
220 // listeners.fireEventSync(itemRemoved, this, item);
223 @SuppressWarnings("unchecked")
225 public <R extends E> R getAtMostOneItemOfClass(Class<R> clazz) {
230 if (clazz.isAssignableFrom(i.getClass())) {
235 if (count==0) return null;
237 throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+count);
242 protected void doDispose() {
246 public String toString() {
247 String s = EString.implode(snapshotArray, "\n");
248 return s != null ? s : "";