Added GECacheKey.toString() to allow debugging hashcode/equals problems
[simantics/platform.git] / bundles / org.simantics.browsing.ui.common / src / org / simantics / browsing / ui / common / internal / GECache.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.internal;
13
14 import gnu.trove.map.hash.THashMap;
15 import gnu.trove.map.hash.TObjectIntHashMap;
16 import gnu.trove.set.hash.THashSet;
17
18 import java.util.Collections;
19 import java.util.Map;
20 import java.util.Set;
21
22 import org.simantics.browsing.ui.NodeContext;
23 import org.simantics.browsing.ui.NodeContext.CacheKey;
24
25 public class GECache implements IGECache {
26
27     final Map<GECacheKey, IGECacheEntry> entries = new THashMap<>();
28     final Map<GECacheKey, Set<UIElementReference>> treeReferences = new THashMap<>();
29
30     final private static class GECacheKey {
31
32         private NodeContext context;
33         private CacheKey<?> key;
34         private int hash;
35
36         GECacheKey(NodeContext context, CacheKey<?> key) {
37             setValues(context, key);
38         }
39
40         GECacheKey(GECacheKey other) {
41             setValues(other.context, other.key);
42         }
43
44         void setValues(NodeContext context, CacheKey<?> key) {
45             if (context == null || key == null) 
46                 throw new IllegalArgumentException("Null context or key is not accepted");
47             this.context = context;
48             this.key = key;
49             this.hash = calcHash();
50         }
51
52         private int calcHash() {
53             return (31 * context.hashCode()) + key.hashCode();
54         }
55
56         @Override
57         public int hashCode() {
58             return hash;
59         }
60
61         @Override
62         public boolean equals(Object object) {
63
64             if (this == object)
65                 return true;
66             else if (object == null)
67                 return false;
68 //            else if (getClass() != object.getClass())
69 //                return false;
70
71             GECacheKey i = (GECacheKey)object;
72
73             return key.equals(i.key) && context.equals(i.context);
74
75         }
76
77         @Override
78         public String toString() {
79             return String.format("%s@%d [key=%s, context=%s]", getClass().getSimpleName(), System.identityHashCode(this), key, context); //$NON-NLS-1$
80         }
81
82     };
83
84     /**
85      * This single instance is used for all get operations from the cache. This
86      * should work since the GE cache is meant to be single-threaded within the
87      * current UI thread, what ever that thread is. For put operations which
88      * store the key, this is not used.
89      */
90     NodeContext getNC = new NodeContext() {
91                 @Override
92         public <T> T getAdapter(Class<T> adapter) {
93                 return null;
94         }
95         
96         @Override
97         public <T> T getConstant(ConstantKey<T> key) {
98                 return null;
99         }
100         
101         @Override
102         public Set<ConstantKey<?>> getKeys() {
103                 return Collections.emptySet();
104         }
105     };
106     CacheKey<?> getCK = new CacheKey<Object>() {
107         @Override
108         public Object processorIdenfitier() {
109                 return this;
110         }
111         };
112     GECacheKey getKey = new GECacheKey(getNC, getCK);
113
114     public <T> IGECacheEntry put(NodeContext context, CacheKey<T> key, T value) {
115         IGECacheEntry entry = new GECacheEntry(context, key, value);
116         entries.put(new GECacheKey(context, key), entry);
117         return entry;
118     }
119
120     @SuppressWarnings("unchecked")
121     public <T> T get(NodeContext context, CacheKey<T> key) {
122         getKey.setValues(context, key);
123         IGECacheEntry entry = entries.get(getKey);
124         return entry != null ? (T) entry.getValue() : null;
125     }
126
127     @Override
128     public <T> IGECacheEntry getEntry(NodeContext context, CacheKey<T> key) {
129         assert(context != null);
130         assert(key != null);
131         getKey.setValues(context, key);
132         return entries.get(getKey);
133     }
134
135     @Override
136     public <T> void remove(NodeContext context, CacheKey<T> key) {
137         getKey.setValues(context, key);
138         entries.remove(getKey);
139     }
140
141     @Override
142     public <T> Set<UIElementReference> getTreeReference(NodeContext context, CacheKey<T> key) {
143         assert(context != null);
144         assert(key != null);
145         getKey.setValues(context, key);
146         return treeReferences.get(getKey);
147     }
148
149     @Override
150     public <T> void putTreeReference(NodeContext context, CacheKey<T> key, UIElementReference reference) {
151         assert(context != null);
152         assert(key != null);
153         getKey.setValues(context, key);
154         Set<UIElementReference> refs = treeReferences.get(getKey);
155         if (refs != null) {
156             refs.add(reference);
157         } else {
158             refs = new THashSet<UIElementReference>(4);
159             refs.add(reference);
160             treeReferences.put(new GECacheKey(getKey), refs);
161         }
162     }
163
164     @Override
165     public <T> Set<UIElementReference> removeTreeReference(NodeContext context, CacheKey<T> key) {
166         assert(context != null);
167         assert(key != null);
168         getKey.setValues(context, key);
169         return treeReferences.remove(getKey);
170     }
171     
172     @Override
173     public boolean isShown(NodeContext context) {
174         return references.get(context) > 0;
175     }
176
177     private TObjectIntHashMap<NodeContext> references = new TObjectIntHashMap<>();
178     
179     @Override
180     synchronized public void incRef(NodeContext context) {
181         int exist = references.get(context);
182         references.put(context, exist+1);
183     }
184     
185     @Override
186     synchronized public void decRef(NodeContext context) {
187         int exist = references.get(context);
188         references.put(context, exist-1);
189         if(exist == 1) {
190                 references.remove(context);
191         }
192     }
193     
194     public void dispose() {
195         references.clear();
196         entries.clear();
197         treeReferences.clear();
198     }
199     
200 }