--- /dev/null
+/*******************************************************************************\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
+package org.simantics.browsing.ui.common;\r
+\r
+import java.util.ArrayDeque;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Deque;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+/**\r
+ * The default implementation of <code>EvaluatorData</code>.\r
+ * \r
+ * <p>\r
+ * <code>Evaluator</code>s can be added by invoking\r
+ * {@link #addEvaluator(Class, org.simantics.browsing.ui.common.EvaluatorData.Evaluator)\r
+ * or {@link #addEvaluators(Class, Collection)}. The specified\r
+ * <code>Evaluator</code> or collection of <code>Evaluator</code>s are bound to\r
+ * the specified Java class. The reason for such a binding is that the\r
+ * implementation of {@link #get(Object)} is based on comparing the input object\r
+ * class to the classes specified to the <code>addEvaluator</code> methods. The\r
+ * comparison works so that an <code>Evaluator</code> will be added to the\r
+ * result of {@link #get(Object)} if:\r
+ * </p>\r
+ * <ul>\r
+ * <li>the input object is assignable to the <code>Evaluator</code>'s class\r
+ * </ul>\r
+ * \r
+ * <p>\r
+ * This implementation keeps a <code>Class</code>→<code>Evaluator</code>\r
+ * cache of previous results thus quickly producing results for a given input\r
+ * object class after the result has been cached. Note that there is no way to\r
+ * reset the cache, i.e. this class is intended to be initialized once, used\r
+ * after that and finally thrown away. Do not add evaluators once in use, they\r
+ * may or may not be reflected in the results.\r
+ * </p>\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class EvaluatorDataImpl implements EvaluatorData {\r
+\r
+ private static final boolean DEBUG = false;\r
+\r
+ /**\r
+ * Contains only the original evaluator associations made directly by the\r
+ * client. This is used for initializing the <code>evaluators</code> cache\r
+ * for different inputs.\r
+ */\r
+ protected HashMap<Class<?>, Collection<Evaluator>> originals = new HashMap<Class<?>, Collection<Evaluator>>();\r
+\r
+ /**\r
+ * A cache that is only modified by\r
+ * {@link #findAndCacheEvaluatorsForClass(Class)}. This map is not filled in\r
+ * by client's initialization efforts.\r
+ */\r
+ protected HashMap<Class<?>, Collection<Evaluator>> evaluators = new HashMap<Class<?>, Collection<Evaluator>>();\r
+\r
+ private Object browseContext;\r
+ \r
+ public EvaluatorDataImpl() {\r
+ }\r
+ \r
+ public void setBrowseContext(Object browseContext) {\r
+ this.browseContext = browseContext;\r
+ }\r
+ \r
+ @Override\r
+ public Evaluator newEvaluator() {\r
+ return new EvaluatorImpl();\r
+ }\r
+\r
+ private synchronized void addEvaluator(HashMap<Class<?>, Collection<Evaluator>> map, Class<?> clazz, Evaluator eval) {\r
+ Collection<Evaluator> es = map.get(clazz);\r
+ if (es == null) {\r
+ es = new ArrayList<Evaluator>();\r
+ map.put(clazz, es);\r
+ }\r
+ es.add(eval);\r
+ if (DEBUG) {\r
+// new Exception().printStackTrace();\r
+ System.out.println("EvaluatorDataImpl: ADDED Evaluator: " + clazz + ": " + eval);\r
+ }\r
+ }\r
+\r
+ private synchronized void addEvaluators(HashMap<Class<?>, Collection<Evaluator>> map, Class<?> clazz, Collection<Evaluator> evals) {\r
+ Collection<Evaluator> es = map.get(clazz);\r
+ if (es == null) {\r
+ es = new ArrayList<Evaluator>();\r
+ map.put(clazz, es);\r
+ }\r
+ es.addAll(evals);\r
+ if (DEBUG)\r
+ System.out.println("EvaluatorDataImpl: ADDED Evaluators: " + clazz + ": " + evals);\r
+ }\r
+\r
+ public synchronized void addEvaluator(Class<?> clazz, Evaluator eval) {\r
+ addEvaluator(originals, clazz, eval);\r
+ }\r
+\r
+ public synchronized void addEvaluators(Class<?> clazz, Collection<Evaluator> evals) {\r
+ addEvaluators(originals, clazz, evals);\r
+ }\r
+\r
+ @Override\r
+ public Collection<Evaluator> get(Object object) {\r
+ Class<?> clazz = object.getClass();\r
+ Collection<Evaluator> evaluator = evaluators.get(clazz);\r
+ if (evaluator != null) {\r
+ if (DEBUG)\r
+ System.out.println("EvaluatorDataImpl: get cached: " + clazz + ": " + evaluator);\r
+ return evaluator;\r
+ }\r
+\r
+ Collection<Evaluator> evals = findAndCacheEvaluatorsForClass(clazz);\r
+ if (DEBUG) {\r
+ System.out.println("EvaluatorDataImpl: get: " + clazz + ": " + evals);\r
+ if (evals.isEmpty()) {\r
+ System.out.println("EvaluatorDataImpl: no match, evaluators available:");\r
+ for (Map.Entry<Class<?>, Collection<Evaluator>> e : evaluators.entrySet()) {\r
+ if (!e.getValue().isEmpty())\r
+ System.out.println(" " + e.getKey() + " : " + e.getValue());\r
+ }\r
+ }\r
+ }\r
+ return evals;\r
+ }\r
+\r
+ private synchronized Collection<Evaluator> findAndCacheEvaluatorsForClass(Class<?> clazz) {\r
+ if (DEBUG)\r
+ System.out.println("EvaluatorDataImpl: caching evaluators for " + clazz);\r
+ Set<Class<?>> usedClasses = new HashSet<Class<?>>();\r
+ Set<Evaluator> used = new HashSet<Evaluator>();\r
+ Collection<Evaluator> result = new ArrayList<Evaluator>();\r
+ Deque<Class<?>> fifo = new ArrayDeque<Class<?>>();\r
+ fifo.add(clazz);\r
+ while (!fifo.isEmpty()) {\r
+ Class<?> c = fifo.removeFirst();\r
+ if (!usedClasses.add(c))\r
+ continue;\r
+ Class<?> superClass = c.getSuperclass();\r
+ if (superClass != null)\r
+ fifo.addLast(superClass);\r
+ for (Class<?> i : c.getInterfaces())\r
+ fifo.addLast(i);\r
+ Collection<Evaluator> evals = originals.get(c);\r
+ if (evals != null)\r
+ for (Evaluator eval : evals)\r
+ if (used.add(eval))\r
+ result.add(eval);\r
+ }\r
+ if (result.isEmpty()) {\r
+ result = Collections.emptyList();\r
+ }\r
+ evaluators.put(clazz, result);\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public Collection<EvaluatorEntry> enumEvaluators() {\r
+ Collection<EvaluatorEntry> result = new ArrayList<EvaluatorEntry>();\r
+ for (Map.Entry<Class<?>, Collection<Evaluator>> entry : originals.entrySet()) {\r
+ result.add(new EvaluatorEntryImpl(entry.getKey(), entry.getValue()));\r
+ }\r
+ return result;\r
+ }\r
+\r
+ static class EvaluatorEntryImpl implements EvaluatorEntry {\r
+ private final Class<?> clazz;\r
+ private final Collection<Evaluator> evaluators;\r
+\r
+ public EvaluatorEntryImpl(Class<?> clazz, Collection<Evaluator> evaluators) {\r
+ this.clazz = clazz;\r
+ this.evaluators = evaluators;\r
+ }\r
+\r
+ @Override\r
+ public Class<?> getClazz() {\r
+ return clazz;\r
+ }\r
+\r
+ @Override\r
+ public Collection<Evaluator> getEvaluators() {\r
+ return evaluators;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "class " + clazz.getSimpleName();\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public <T> T getBrowseContext() {\r
+ return (T)browseContext;\r
+ }\r
+ \r
+}\r