]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/NodeContextBuilder.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.browsing.ui.common / src / org / simantics / browsing / ui / common / NodeContextBuilder.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.Collections;
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.eclipse.core.runtime.IAdaptable;
20 import org.simantics.browsing.ui.BuiltinKeys;
21 import org.simantics.browsing.ui.NodeContext;
22 import org.simantics.browsing.ui.NodeContext.ConstantKey;
23 import org.simantics.browsing.ui.content.Viewpoint;
24 import org.simantics.utils.datastructures.ArrayMap;
25
26 /**
27  * Use this class in {@link Viewpoint}, and particularly
28  * {@link Viewpoint#getChildren()} implementations to construct new INodeContext
29  * instances.
30  * 
31  * @author Antti Villberg
32  * @author Tuukka Lehtonen
33  */
34 public final class NodeContextBuilder {
35
36     private static Set<ConstantKey<?>> SINGLE_INPUT_KEYS =
37         Collections.<ConstantKey<?>> singleton(BuiltinKeys.INPUT);
38
39     MapNodeContext context;
40
41     private static class InputNodeContext implements NodeContext {
42
43         final Object input;
44
45         private InputNodeContext(Object input) {
46             this.input = input;
47         }
48
49         @SuppressWarnings({ "unchecked", "rawtypes" })
50         @Override
51         public Object getAdapter(Class adapter) {
52             if (NodeContext.class.equals(adapter))
53                 return this;
54             if (input != null) {
55                 if (adapter.isAssignableFrom(input.getClass()))
56                     return input;
57                 if (input instanceof IAdaptable)
58                     return ((IAdaptable) input).getAdapter(adapter);
59             }
60             return null;
61         }
62
63         @SuppressWarnings("unchecked")
64         @Override
65         public <T> T getConstant(ConstantKey<T> key) {
66             return key == BuiltinKeys.INPUT ? (T) input : null;
67         }
68
69         @Override
70         public Set<ConstantKey<?>> getKeys() {
71             return SINGLE_INPUT_KEYS;
72         }
73
74         @Override
75         public int hashCode() {
76             return ((input == null) ? 0 : input.hashCode());
77         }
78
79         @Override
80         public boolean equals(Object obj) {
81             if (this == obj)
82                 return true;
83             if (obj == null)
84                 return false;
85             if (getClass() != obj.getClass())
86                 return false;
87             InputNodeContext other = (InputNodeContext) obj;
88             if (input == null) {
89                 if (other.input != null)
90                     return false;
91             } else if (!input.equals(other.input))
92                 return false;
93             return true;
94         }
95
96         @Override
97         public String toString() {
98             return "InputNodeContext(" + hashCode() + ") [" + input + "]";
99         }
100     }
101
102     public static class MapNodeContext implements NodeContext {
103
104         private Map<ConstantKey<?>, Object> data;
105         private int hash;
106
107         private MapNodeContext() {
108         }
109
110         void makeHash() {
111             this.hash = (data == null) ? 0 : data.hashCode();
112         }
113
114         @SuppressWarnings({ "unchecked", "rawtypes" })
115         @Override
116         public Object getAdapter(Class adapter) {
117             if (NodeContext.class.equals(adapter))
118                 return this;
119             Object input = data.get(BuiltinKeys.INPUT);
120             if (input != null) {
121                 if (adapter.isAssignableFrom(input.getClass()))
122                     return input;
123                 if (input instanceof IAdaptable)
124                     return ((IAdaptable) input).getAdapter(adapter);
125             }
126             return null;
127         }
128
129         @SuppressWarnings("unchecked")
130         @Override
131         public <T> T getConstant(ConstantKey<T> key) {
132             return data != null ? (T) data.get(key) : null;
133         }
134
135         @Override
136         public Set<ConstantKey<?>> getKeys() {
137             return data.keySet();
138         }
139
140         @Override
141         public int hashCode() {
142             return hash;
143         }
144
145         @Override
146         public boolean equals(Object obj) {
147             if (this == obj)
148                 return true;
149             if (obj == null)
150                 return false;
151             if (getClass() != obj.getClass())
152                 return false;
153             MapNodeContext other = (MapNodeContext) obj;
154             if (data == null) {
155                 if (other.data != null)
156                     return false;
157             } else if (!data.equals(other.data))
158                 return false;
159             return true;
160         }
161
162         @Override
163         public String toString() {
164             return "MapNodeContext(" + hashCode() + ") [" + (data != null ? data : "") + "]";
165         }
166     }
167
168     public NodeContextBuilder() {
169         this.context = new MapNodeContext();
170     }
171
172     public <T> NodeContextBuilder define(ConstantKey<T> key, T value) {
173         if (context.data == null)
174             context.data = new HashMap<ConstantKey<?>, Object>();
175         context.data.put(key, value);
176         return this;
177     }
178
179     public NodeContext createContext() {
180         context.makeHash();
181         return context;
182     }
183
184     /**
185      * A special case builder for an INodeContext that will only take one
186      * (key,value) pair and insert that into the built INodeContext.
187      * 
188      * <p>
189      * This method works around the need to create NodeContextBuilder instances.
190      * </p>
191      * 
192      * <p>
193      * Notice that your input key and value must properly implement
194      * <code>equals</code> and <code>hashCode</code> in order for
195      * GraphExplorer's automatic node expansion state tracking to work.
196      * </p>
197      * 
198      * @param key
199      * @param value
200      * @return an INodeContext containing a single constant as specified by
201      *         <code>key</code> and <code>value</code>
202      */
203     public static <T> NodeContext buildWithSingleData(ConstantKey<T> key, T value) {
204         MapNodeContext context = new MapNodeContext();
205         context.data = Collections.<ConstantKey<?>, Object>singletonMap(key, value);
206         context.makeHash();
207         return context;
208     }
209
210     /**
211      * A generic case builder for an INodeContext that will take as many
212      * (key,value) pairs as it is given and insert all of them into a map
213      * that is backing the built INodeContext implementation.
214      * 
215      * <p>
216      * This method works around the need to create NodeContextBuilder instances.
217      * </p>
218      * 
219      * <p>
220      * Notice that your input keys and values must properly implement
221      * <code>equals</code> and <code>hashCode</code> in order for
222      * GraphExplorer's automatic node expansion state tracking to work.
223      * </p>
224      * 
225      * @param keyValuePairs
226      * @return an INodeContext containing the specified key,value pairs as its
227      *         constants
228      */
229     public static <T> NodeContext buildWithData(Object... keyValuePairs) {
230         assert keyValuePairs.length % 2 == 0;
231         MapNodeContext context = new MapNodeContext();
232         int size = keyValuePairs.length;
233         Map<ConstantKey<?>, Object> map = new HashMap<ConstantKey<?>, Object>(size);
234
235         for (int i = 0; i < keyValuePairs.length; i += 2) {
236             map.put((ConstantKey<?>) keyValuePairs[i], keyValuePairs[i+1]);
237         }
238
239         context.data = map;
240         context.makeHash();
241         return context;
242     }
243
244     /**
245      * A generic case builder for an INodeContext that will take as many
246      * (key,value) pairs as it is given and insert all of them into a map that
247      * is backing the built INodeContext implementation. This method takes the
248      * keys and values separately, which allows the use of {@link ArrayMap} that
249      * provides a space-optimized Map implementation. Note that the key set of
250      * an {@link ArrayMap} is immutable.
251      * 
252      * <p>
253      * This method works around the need to create NodeContextBuilder instances.
254      * </p>
255      * 
256      * <p>
257      * Notice that your input keys and values must properly implement
258      * <code>equals</code> and <code>hashCode</code> in order for
259      * GraphExplorer's automatic node expansion state tracking to work.
260      * </p>
261      * 
262      * @param keys the keys of the map, must equal values array in size
263      * @param values the values of the map, must equal keys array in size
264      * @return a <code>NodeContext</code> containing the specified key,value
265      *         pairs as its constants
266      */
267     public static <T> NodeContext buildWithData(ConstantKey<?>[] keys, Object[] values) {
268         assert keys.length == values.length;
269         MapNodeContext context = new MapNodeContext();
270         Map<ConstantKey<?>, Object> map = new ArrayMap<ConstantKey<?>, Object>(keys, values);
271         context.data = map;
272         context.makeHash();
273         return context;
274     }
275
276     /**
277      * A special case builder for an INodeContext that will only take one
278      * (key,value) pair and insert that into the built INodeContext.
279      * 
280      * <p>
281      * This method works around the need to create NodeContextBuilder instances.
282      * </p>
283      * 
284      * <p>
285      * Notice that your input object must properly implement <code>equals</code>
286      * and <code>hashCode</code> in order for GraphExplorer's automatic node
287      * expansion state tracking to work.
288      * </p>
289      * 
290      * @param input
291      * @return an INodeContext containing the specified object as with the key
292      *         {@link BuiltinKeys#INPUT}
293      */
294     public static NodeContext buildWithInput(Object input) {
295         return new InputNodeContext(input);
296     }
297
298 }