-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-/*\r
- *\r
- * @author Toni Kalajainen\r
- */\r
-package org.simantics.utils.datastructures.context;\r
-\r
-import java.lang.reflect.Array;\r
-import java.lang.reflect.Method;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.simantics.utils.datastructures.disposable.AbstractDisposable;\r
-import org.simantics.utils.strings.EString;\r
-import org.simantics.utils.threads.Executable;\r
-import org.simantics.utils.threads.IThreadWorkQueue;\r
-import org.simantics.utils.threads.SyncListenerList;\r
-import org.simantics.utils.threads.ThreadUtils;\r
-\r
-/**\r
- * \r
- * TODO Optimize getItemsByClass queries (Create optimizing structures after 1st query)\r
- * TODO Optimize getSingleItem query\r
- *\r
- * @param <E>\r
- */\r
-public class Context<E> extends AbstractDisposable implements IContext<E> {\r
-\r
- protected Set<E> set = new HashSet<E>();\r
- @SuppressWarnings({ "rawtypes" })\r
- protected SyncListenerList<IContextListener> listeners =\r
- new SyncListenerList<IContextListener>(IContextListener.class);\r
- \r
- final Class<E> clazz;\r
- \r
- private E[] snapshotArray;\r
- private Map<Class<? extends E>, Collection<? extends E>> classQueryResultCache = \r
- new HashMap<Class<? extends E>, Collection<? extends E>>();\r
- \r
- public Context(Class<E> clazz) {\r
- this.clazz = clazz;\r
- snapshotArray = createArray(clazz, 0);\r
- }\r
- \r
- @Override\r
- public void add(E item) {\r
- assertNotDisposed();\r
- Executable executables[];\r
- synchronized(this) {\r
- if (set.contains(item))\r
- throw new IllegalArgumentException("Context already contains item "+item);\r
- \r
- set.add(item);\r
- snapshotArray = createSnapshot(set);\r
- \r
- executables = listeners.getExecutables(itemAdded, this, item);\r
- \r
- Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();\r
- while (i.hasNext()) {\r
- Class<? extends E> c = i.next();\r
- if (c.isInstance(item))\r
- i.remove(); \r
- }\r
- }\r
- ThreadUtils.multiSyncExec(executables);\r
- }\r
-\r
- @Override\r
- public boolean remove(E item) {\r
- assertNotDisposed();\r
- Executable executables[]; \r
- synchronized(this) {\r
- if (!set.remove(item)) \r
- return false;\r
- snapshotArray = createSnapshot(set);\r
- executables = listeners.getExecutables(itemRemoved, this, item);\r
- \r
- Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();\r
- while (i.hasNext()) {\r
- Class<? extends E> c = i.next();\r
- if (c.isInstance(item))\r
- i.remove(); \r
- } \r
- }\r
- ThreadUtils.multiSyncExec(executables);\r
- return true;\r
- }\r
- \r
- @Override\r
- public synchronized boolean contains(E item) {\r
- assertNotDisposed();\r
- return set.contains(item);\r
- }\r
-\r
- @Override\r
- public void clear() {\r
- assertNotDisposed();\r
- classQueryResultCache.clear();\r
- ArrayList<Executable> executables = new ArrayList<Executable>();\r
- synchronized(this) {\r
- if (set.isEmpty())\r
- return;\r
-\r
- for (E item : snapshotArray)\r
- listeners.addExecutables(executables, itemRemoved, this, item);\r
- \r
- set.clear();\r
- this.snapshotArray = createSnapshot(set);\r
- }\r
- ThreadUtils.multiSyncExec(executables);\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- @Override\r
- public synchronized <R extends E> Collection<R> getItemsByClass(Class<R> clazz) {\r
- assertNotDisposed(); \r
- Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);\r
- if (result!=null)\r
- return result;\r
- result = new ArrayList<R>();\r
- for (E i : set)\r
- if (clazz.isAssignableFrom(i.getClass()))\r
- result.add((R)i);\r
- classQueryResultCache.put(clazz, result);\r
- return result;\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- @Override\r
- public synchronized <R> boolean containsItemByClass(Class<R> clazz) {\r
- assertNotDisposed();\r
- Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);\r
- if (result!=null) return !result.isEmpty();\r
- for (E i : set)\r
- if (clazz.isAssignableFrom(i.getClass()))\r
- return true;\r
- return false;\r
- }\r
- \r
- @SuppressWarnings({ "unchecked" })\r
- private <R extends E> R[] createArray(Class<R> clazz, int length)\r
- {\r
- return (R[]) Array.newInstance(clazz, length);\r
- } \r
- \r
- E[] createSnapshot(Set<E> set) {\r
- E[] result = createArray(clazz, set.size());\r
- int index = 0;\r
- for (E i : set)\r
- result[index++] = i; \r
- return result;\r
- } \r
- \r
- public synchronized <R extends E> R getSingleItem(Class<R> clazz)\r
- {\r
- assertNotDisposed();\r
- Collection<R> result = getItemsByClass(clazz);\r
- if (result.size()==1)\r
- return result.iterator().next();\r
- throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+result.size()); \r
- }\r
-\r
- @Override\r
- public E[] toArray() {\r
- assertNotDisposed();\r
- return snapshotArray;\r
- }\r
- \r
- @Override\r
- public void addContextListener(IContextListener<E> listener) {\r
- assertNotDisposed();\r
- listeners.add(listener);\r
- }\r
-\r
- @Override\r
- public void removeContextListener(IContextListener<E> listener) {\r
- assertNotDisposed();\r
- listeners.remove(listener);\r
- } \r
- \r
- @Override\r
- public void addContextListener(IThreadWorkQueue thread,\r
- IContextListener<E> listener) {\r
- assertNotDisposed();\r
- listeners.add(thread, listener);\r
- }\r
-\r
- @Override\r
- public void removeContextListener(IThreadWorkQueue thread,\r
- IContextListener<E> listener) {\r
- assertNotDisposed();\r
- listeners.remove(thread, listener);\r
- } \r
-\r
- private static Method itemAdded = SyncListenerList.getMethod(IContextListener.class, "itemAdded");\r
- private static Method itemRemoved = SyncListenerList.getMethod(IContextListener.class, "itemRemoved");\r
-\r
-// private void fireItemAdded(E item)\r
-// {\r
-// listeners.fireEventSync(itemAdded, this, item);\r
-// }\r
-// \r
-// private void fireItemRemoved(E item)\r
-// {\r
-// listeners.fireEventSync(itemRemoved, this, item);\r
-// }\r
-\r
- @SuppressWarnings("unchecked")\r
- @Override\r
- public <R extends E> R getAtMostOneItemOfClass(Class<R> clazz) {\r
- assertNotDisposed();\r
- int count = 0;\r
- R r = null;\r
- for (E i : set) {\r
- if (clazz.isAssignableFrom(i.getClass())) {\r
- ++count;\r
- r = (R) i;\r
- }\r
- }\r
- if (count==0) return null;\r
- if (count>1)\r
- throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+count);\r
- return r;\r
- }\r
- \r
- @Override\r
- protected void doDispose() {\r
- }\r
-\r
- @Override\r
- public String toString() {\r
- String s = EString.implode(snapshotArray, "\n");\r
- return s != null ? s : "";\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+/*
+ *
+ * @author Toni Kalajainen
+ */
+package org.simantics.utils.datastructures.context;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.simantics.utils.datastructures.disposable.AbstractDisposable;
+import org.simantics.utils.strings.EString;
+import org.simantics.utils.threads.Executable;
+import org.simantics.utils.threads.IThreadWorkQueue;
+import org.simantics.utils.threads.SyncListenerList;
+import org.simantics.utils.threads.ThreadUtils;
+
+/**
+ *
+ * TODO Optimize getItemsByClass queries (Create optimizing structures after 1st query)
+ * TODO Optimize getSingleItem query
+ *
+ * @param <E>
+ */
+public class Context<E> extends AbstractDisposable implements IContext<E> {
+
+ protected Set<E> set = new HashSet<E>();
+ @SuppressWarnings({ "rawtypes" })
+ protected SyncListenerList<IContextListener> listeners =
+ new SyncListenerList<IContextListener>(IContextListener.class);
+
+ final Class<E> clazz;
+
+ private E[] snapshotArray;
+ private Map<Class<? extends E>, Collection<? extends E>> classQueryResultCache =
+ new HashMap<Class<? extends E>, Collection<? extends E>>();
+
+ public Context(Class<E> clazz) {
+ this.clazz = clazz;
+ snapshotArray = createArray(clazz, 0);
+ }
+
+ @Override
+ public void add(E item) {
+ assertNotDisposed();
+ Executable executables[];
+ synchronized(this) {
+ if (set.contains(item))
+ throw new IllegalArgumentException("Context already contains item "+item);
+
+ set.add(item);
+ snapshotArray = createSnapshot(set);
+
+ executables = listeners.getExecutables(itemAdded, this, item);
+
+ Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();
+ while (i.hasNext()) {
+ Class<? extends E> c = i.next();
+ if (c.isInstance(item))
+ i.remove();
+ }
+ }
+ ThreadUtils.multiSyncExec(executables);
+ }
+
+ @Override
+ public boolean remove(E item) {
+ assertNotDisposed();
+ Executable executables[];
+ synchronized(this) {
+ if (!set.remove(item))
+ return false;
+ snapshotArray = createSnapshot(set);
+ executables = listeners.getExecutables(itemRemoved, this, item);
+
+ Iterator<Class<? extends E>> i = classQueryResultCache.keySet().iterator();
+ while (i.hasNext()) {
+ Class<? extends E> c = i.next();
+ if (c.isInstance(item))
+ i.remove();
+ }
+ }
+ ThreadUtils.multiSyncExec(executables);
+ return true;
+ }
+
+ @Override
+ public synchronized boolean contains(E item) {
+ assertNotDisposed();
+ return set.contains(item);
+ }
+
+ @Override
+ public void clear() {
+ assertNotDisposed();
+ classQueryResultCache.clear();
+ ArrayList<Executable> executables = new ArrayList<Executable>();
+ synchronized(this) {
+ if (set.isEmpty())
+ return;
+
+ for (E item : snapshotArray)
+ listeners.addExecutables(executables, itemRemoved, this, item);
+
+ set.clear();
+ this.snapshotArray = createSnapshot(set);
+ }
+ ThreadUtils.multiSyncExec(executables);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public synchronized <R extends E> Collection<R> getItemsByClass(Class<R> clazz) {
+ assertNotDisposed();
+ Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);
+ if (result!=null)
+ return result;
+ result = new ArrayList<R>();
+ for (E i : set)
+ if (clazz.isAssignableFrom(i.getClass()))
+ result.add((R)i);
+ classQueryResultCache.put(clazz, result);
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public synchronized <R> boolean containsItemByClass(Class<R> clazz) {
+ assertNotDisposed();
+ Collection<R> result = (Collection<R>) classQueryResultCache.get(clazz);
+ if (result!=null) return !result.isEmpty();
+ for (E i : set)
+ if (clazz.isAssignableFrom(i.getClass()))
+ return true;
+ return false;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ private <R extends E> R[] createArray(Class<R> clazz, int length)
+ {
+ return (R[]) Array.newInstance(clazz, length);
+ }
+
+ E[] createSnapshot(Set<E> set) {
+ E[] result = createArray(clazz, set.size());
+ int index = 0;
+ for (E i : set)
+ result[index++] = i;
+ return result;
+ }
+
+ public synchronized <R extends E> R getSingleItem(Class<R> clazz)
+ {
+ assertNotDisposed();
+ Collection<R> result = getItemsByClass(clazz);
+ if (result.size()==1)
+ return result.iterator().next();
+ throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+result.size());
+ }
+
+ @Override
+ public E[] toArray() {
+ assertNotDisposed();
+ return snapshotArray;
+ }
+
+ @Override
+ public void addContextListener(IContextListener<E> listener) {
+ assertNotDisposed();
+ listeners.add(listener);
+ }
+
+ @Override
+ public void removeContextListener(IContextListener<E> listener) {
+ assertNotDisposed();
+ listeners.remove(listener);
+ }
+
+ @Override
+ public void addContextListener(IThreadWorkQueue thread,
+ IContextListener<E> listener) {
+ assertNotDisposed();
+ listeners.add(thread, listener);
+ }
+
+ @Override
+ public void removeContextListener(IThreadWorkQueue thread,
+ IContextListener<E> listener) {
+ assertNotDisposed();
+ listeners.remove(thread, listener);
+ }
+
+ private static Method itemAdded = SyncListenerList.getMethod(IContextListener.class, "itemAdded");
+ private static Method itemRemoved = SyncListenerList.getMethod(IContextListener.class, "itemRemoved");
+
+// private void fireItemAdded(E item)
+// {
+// listeners.fireEventSync(itemAdded, this, item);
+// }
+//
+// private void fireItemRemoved(E item)
+// {
+// listeners.fireEventSync(itemRemoved, this, item);
+// }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends E> R getAtMostOneItemOfClass(Class<R> clazz) {
+ assertNotDisposed();
+ int count = 0;
+ R r = null;
+ for (E i : set) {
+ if (clazz.isAssignableFrom(i.getClass())) {
+ ++count;
+ r = (R) i;
+ }
+ }
+ if (count==0) return null;
+ if (count>1)
+ throw new RuntimeException("one "+clazz.getName()+" expected in Context, got "+count);
+ return r;
+ }
+
+ @Override
+ protected void doDispose() {
+ }
+
+ @Override
+ public String toString() {
+ String s = EString.implode(snapshotArray, "\n");
+ return s != null ? s : "";
+ }
+
+}