]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/EvaluatorDataImpl.java
Still working for multiple readers
[simantics/platform.git] / bundles / org.simantics.browsing.ui.common / src / org / simantics / browsing / ui / common / EvaluatorDataImpl.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.browsing.ui.common;
13
14 import java.util.ArrayDeque;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Deque;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Map;
22 import java.util.Set;
23
24 /**
25  * The default implementation of <code>EvaluatorData</code>.
26  * 
27  * <p>
28  * <code>Evaluator</code>s can be added by invoking
29  * {@link #addEvaluator(Class, org.simantics.browsing.ui.common.EvaluatorData.Evaluator)
30  * or {@link #addEvaluators(Class, Collection)}. The specified
31  * <code>Evaluator</code> or collection of <code>Evaluator</code>s are bound to
32  * the specified Java class. The reason for such a binding is that the
33  * implementation of {@link #get(Object)} is based on comparing the input object
34  * class to the classes specified to the <code>addEvaluator</code> methods. The
35  * comparison works so that an <code>Evaluator</code> will be added to the
36  * result of {@link #get(Object)} if:
37  * </p>
38  * <ul>
39  * <li>the input object is assignable to the <code>Evaluator</code>'s class
40  * </ul>
41  * 
42  * <p>
43  * This implementation keeps a <code>Class</code>&#8594;<code>Evaluator</code>
44  * cache of previous results thus quickly producing results for a given input
45  * object class after the result has been cached. Note that there is no way to
46  * reset the cache, i.e. this class is intended to be initialized once, used
47  * after that and finally thrown away. Do not add evaluators once in use, they
48  * may or may not be reflected in the results.
49  * </p>
50  * 
51  * @author Tuukka Lehtonen
52  */
53 public class EvaluatorDataImpl implements EvaluatorData {
54
55     private static final boolean DEBUG = false;
56
57     /**
58      * Contains only the original evaluator associations made directly by the
59      * client. This is used for initializing the <code>evaluators</code> cache
60      * for different inputs.
61      */
62     protected HashMap<Class<?>, Collection<Evaluator>> originals = new HashMap<Class<?>, Collection<Evaluator>>();
63
64     /**
65      * A cache that is only modified by
66      * {@link #findAndCacheEvaluatorsForClass(Class)}. This map is not filled in
67      * by client's initialization efforts.
68      */
69     protected HashMap<Class<?>, Collection<Evaluator>> evaluators = new HashMap<Class<?>, Collection<Evaluator>>();
70
71     private Object browseContext;
72     
73     public EvaluatorDataImpl() {
74     }
75     
76     public void setBrowseContext(Object browseContext) {
77         this.browseContext = browseContext;
78     }
79     
80     @Override
81     public Evaluator newEvaluator() {
82         return new EvaluatorImpl();
83     }
84
85     private synchronized void addEvaluator(HashMap<Class<?>, Collection<Evaluator>> map, Class<?> clazz, Evaluator eval) {
86         Collection<Evaluator> es = map.get(clazz);
87         if (es == null) {
88             es = new ArrayList<Evaluator>();
89             map.put(clazz, es);
90         }
91         es.add(eval);
92         if (DEBUG) {
93 //          new Exception().printStackTrace();
94             System.out.println("EvaluatorDataImpl: ADDED Evaluator: " + clazz + ": " + eval);
95         }
96     }
97
98     private synchronized void addEvaluators(HashMap<Class<?>, Collection<Evaluator>> map, Class<?> clazz, Collection<Evaluator> evals) {
99         Collection<Evaluator> es = map.get(clazz);
100         if (es == null) {
101             es = new ArrayList<Evaluator>();
102             map.put(clazz, es);
103         }
104         es.addAll(evals);
105         if (DEBUG)
106             System.out.println("EvaluatorDataImpl: ADDED Evaluators: " + clazz + ": " + evals);
107     }
108
109     public synchronized void addEvaluator(Class<?> clazz, Evaluator eval) {
110         addEvaluator(originals, clazz, eval);
111     }
112
113     public synchronized void addEvaluators(Class<?> clazz, Collection<Evaluator> evals) {
114         addEvaluators(originals, clazz, evals);
115     }
116
117     @Override
118     public Collection<Evaluator> get(Object object) {
119         Class<?> clazz = object.getClass();
120         Collection<Evaluator> evaluator = evaluators.get(clazz);
121         if (evaluator != null) {
122             if (DEBUG)
123                 System.out.println("EvaluatorDataImpl: get cached: " + clazz + ": " + evaluator);
124             return evaluator;
125         }
126
127         Collection<Evaluator> evals = findAndCacheEvaluatorsForClass(clazz);
128         if (DEBUG) {
129             System.out.println("EvaluatorDataImpl: get: " + clazz + ": " + evals);
130             if (evals.isEmpty()) {
131                 System.out.println("EvaluatorDataImpl: no match, evaluators available:");
132                 for (Map.Entry<Class<?>, Collection<Evaluator>> e : evaluators.entrySet()) {
133                     if (!e.getValue().isEmpty())
134                         System.out.println("    " + e.getKey() + " : " + e.getValue());
135                 }
136             }
137         }
138         return evals;
139     }
140
141     private synchronized Collection<Evaluator> findAndCacheEvaluatorsForClass(Class<?> clazz) {
142         if (DEBUG)
143             System.out.println("EvaluatorDataImpl: caching evaluators for " + clazz);
144         Set<Class<?>> usedClasses = new HashSet<Class<?>>();
145         Set<Evaluator> used = new HashSet<Evaluator>();
146         Collection<Evaluator> result = new ArrayList<Evaluator>();
147         Deque<Class<?>> fifo = new ArrayDeque<Class<?>>();
148         fifo.add(clazz);
149         while (!fifo.isEmpty()) {
150             Class<?> c = fifo.removeFirst();
151             if (!usedClasses.add(c))
152                 continue;
153             Class<?> superClass = c.getSuperclass();
154             if (superClass != null)
155                 fifo.addLast(superClass);
156             for (Class<?> i : c.getInterfaces())
157                 fifo.addLast(i);
158             Collection<Evaluator> evals = originals.get(c);
159             if (evals != null)
160                 for (Evaluator eval : evals)
161                     if (used.add(eval))
162                         result.add(eval);
163         }
164         if (result.isEmpty()) {
165             result = Collections.emptyList();
166         }
167         evaluators.put(clazz, result);
168         return result;
169     }
170
171     @Override
172     public Collection<EvaluatorEntry> enumEvaluators() {
173         Collection<EvaluatorEntry> result = new ArrayList<EvaluatorEntry>();
174         for (Map.Entry<Class<?>, Collection<Evaluator>> entry : originals.entrySet()) {
175             result.add(new EvaluatorEntryImpl(entry.getKey(), entry.getValue()));
176         }
177         return result;
178     }
179
180     static class EvaluatorEntryImpl implements EvaluatorEntry {
181         private final Class<?>              clazz;
182         private final Collection<Evaluator> evaluators;
183
184         public EvaluatorEntryImpl(Class<?> clazz, Collection<Evaluator> evaluators) {
185             this.clazz = clazz;
186             this.evaluators = evaluators;
187         }
188
189         @Override
190         public Class<?> getClazz() {
191             return clazz;
192         }
193
194         @Override
195         public Collection<Evaluator> getEvaluators() {
196             return evaluators;
197         }
198
199         @Override
200         public String toString() {
201             return "class " + clazz.getSimpleName();
202         }
203     }
204
205     @SuppressWarnings("unchecked")
206     @Override
207     public <T> T getBrowseContext() {
208         return (T)browseContext;
209     }
210     
211 }