/******************************************************************************* * 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 *******************************************************************************/ package org.simantics.browsing.ui.common; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * The default implementation of EvaluatorData. * *

* Evaluators can be added by invoking * {@link #addEvaluator(Class, org.simantics.browsing.ui.common.EvaluatorData.Evaluator) * or {@link #addEvaluators(Class, Collection)}. The specified * Evaluator or collection of Evaluators are bound to * the specified Java class. The reason for such a binding is that the * implementation of {@link #get(Object)} is based on comparing the input object * class to the classes specified to the addEvaluator methods. The * comparison works so that an Evaluator will be added to the * result of {@link #get(Object)} if: *

* * *

* This implementation keeps a ClassEvaluator * cache of previous results thus quickly producing results for a given input * object class after the result has been cached. Note that there is no way to * reset the cache, i.e. this class is intended to be initialized once, used * after that and finally thrown away. Do not add evaluators once in use, they * may or may not be reflected in the results. *

* * @author Tuukka Lehtonen */ public class EvaluatorDataImpl implements EvaluatorData { private static final boolean DEBUG = false; /** * Contains only the original evaluator associations made directly by the * client. This is used for initializing the evaluators cache * for different inputs. */ protected HashMap, Collection> originals = new HashMap, Collection>(); /** * A cache that is only modified by * {@link #findAndCacheEvaluatorsForClass(Class)}. This map is not filled in * by client's initialization efforts. */ protected HashMap, Collection> evaluators = new HashMap, Collection>(); private Object browseContext; public EvaluatorDataImpl() { } public void setBrowseContext(Object browseContext) { this.browseContext = browseContext; } @Override public Evaluator newEvaluator() { return new EvaluatorImpl(); } private synchronized void addEvaluator(HashMap, Collection> map, Class clazz, Evaluator eval) { Collection es = map.get(clazz); if (es == null) { es = new ArrayList(); map.put(clazz, es); } es.add(eval); if (DEBUG) { // new Exception().printStackTrace(); System.out.println("EvaluatorDataImpl: ADDED Evaluator: " + clazz + ": " + eval); } } private synchronized void addEvaluators(HashMap, Collection> map, Class clazz, Collection evals) { Collection es = map.get(clazz); if (es == null) { es = new ArrayList(); map.put(clazz, es); } es.addAll(evals); if (DEBUG) System.out.println("EvaluatorDataImpl: ADDED Evaluators: " + clazz + ": " + evals); } public synchronized void addEvaluator(Class clazz, Evaluator eval) { addEvaluator(originals, clazz, eval); } public synchronized void addEvaluators(Class clazz, Collection evals) { addEvaluators(originals, clazz, evals); } @Override public Collection get(Object object) { Class clazz = object.getClass(); Collection evaluator = evaluators.get(clazz); if (evaluator != null) { if (DEBUG) System.out.println("EvaluatorDataImpl: get cached: " + clazz + ": " + evaluator); return evaluator; } Collection evals = findAndCacheEvaluatorsForClass(clazz); if (DEBUG) { System.out.println("EvaluatorDataImpl: get: " + clazz + ": " + evals); if (evals.isEmpty()) { System.out.println("EvaluatorDataImpl: no match, evaluators available:"); for (Map.Entry, Collection> e : evaluators.entrySet()) { if (!e.getValue().isEmpty()) System.out.println(" " + e.getKey() + " : " + e.getValue()); } } } return evals; } private synchronized Collection findAndCacheEvaluatorsForClass(Class clazz) { if (DEBUG) System.out.println("EvaluatorDataImpl: caching evaluators for " + clazz); Set> usedClasses = new HashSet>(); Set used = new HashSet(); Collection result = new ArrayList(); Deque> fifo = new ArrayDeque>(); fifo.add(clazz); while (!fifo.isEmpty()) { Class c = fifo.removeFirst(); if (!usedClasses.add(c)) continue; Class superClass = c.getSuperclass(); if (superClass != null) fifo.addLast(superClass); for (Class i : c.getInterfaces()) fifo.addLast(i); Collection evals = originals.get(c); if (evals != null) for (Evaluator eval : evals) if (used.add(eval)) result.add(eval); } if (result.isEmpty()) { result = Collections.emptyList(); } evaluators.put(clazz, result); return result; } @Override public Collection enumEvaluators() { Collection result = new ArrayList(); for (Map.Entry, Collection> entry : originals.entrySet()) { result.add(new EvaluatorEntryImpl(entry.getKey(), entry.getValue())); } return result; } static class EvaluatorEntryImpl implements EvaluatorEntry { private final Class clazz; private final Collection evaluators; public EvaluatorEntryImpl(Class clazz, Collection evaluators) { this.clazz = clazz; this.evaluators = evaluators; } @Override public Class getClazz() { return clazz; } @Override public Collection getEvaluators() { return evaluators; } @Override public String toString() { return "class " + clazz.getSimpleName(); } } @SuppressWarnings("unchecked") @Override public T getBrowseContext() { return (T)browseContext; } }