]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/ISelectionUtils.java
Sync git svn branch with SVN repository r33269.
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / ISelectionUtils.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 /*\r
13  * 25.8.2006\r
14  */\r
15 package org.simantics.utils.ui;\r
16 \r
17 import java.util.ArrayList;\r
18 import java.util.Collection;\r
19 import java.util.Collections;\r
20 import java.util.HashMap;\r
21 import java.util.HashSet;\r
22 import java.util.List;\r
23 import java.util.Map;\r
24 import java.util.Set;\r
25 \r
26 import org.eclipse.core.runtime.Assert;\r
27 import org.eclipse.core.runtime.IAdaptable;\r
28 import org.eclipse.jface.viewers.ISelection;\r
29 import org.eclipse.jface.viewers.IStructuredSelection;\r
30 import org.eclipse.jface.viewers.StructuredSelection;\r
31 import org.simantics.utils.datastructures.hints.IHintContext;\r
32 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
33 \r
34 /**\r
35  * ISelectionUtils contains static utility methods for dealing with the\r
36  * ISelection interface.\r
37  * \r
38  * @author Toni Kalajainen\r
39  */\r
40 public class ISelectionUtils<T> {\r
41 \r
42     /**\r
43      * This method converts selection sel for to collection of instances of T.\r
44      * All elements of the selection are assumed to be instances of T.\r
45      * \r
46      * @param <T> type of interest\r
47      * @param sel selection\r
48      * @return list\r
49      * @throws ClassCastException if the selection contains elements not\r
50      *         instance of T\r
51      */\r
52     @SuppressWarnings("unchecked")\r
53     public static <T> List<T> convertSelection(ISelection sel) throws ClassCastException\r
54     {\r
55         if (sel == null || sel.isEmpty() || (!(sel instanceof IStructuredSelection)))\r
56             return Collections.emptyList();\r
57 \r
58         List<T> result = new ArrayList<T>();\r
59         //Class<T> tClass = (Class<T>)result.getClass().getTypeParameters()[0].getBounds()[0];\r
60         IStructuredSelection ss = (IStructuredSelection) sel;\r
61         for (Object o : ss.toArray())\r
62             //if (tClass.isAssignableFrom(o.getClass()))\r
63             result.add((T)o);\r
64         return result;\r
65     }\r
66 \r
67     /**\r
68      * This method reads selection sel for all instances of T.\r
69      * All elements of the selection are assumed to be instances of T.\r
70      * \r
71      * @param <T> type of interest\r
72      * @param sel selection\r
73      * @return set\r
74      * @throws ClassCastException if the selection contains elements not\r
75      *         instance of T\r
76      */\r
77     @SuppressWarnings("unchecked")\r
78     public static <T> Set<T> convertSetSelection(ISelection sel) throws ClassCastException\r
79     {\r
80         if (sel.isEmpty() || (!(sel instanceof IStructuredSelection)))\r
81             return Collections.emptySet();\r
82 \r
83         Set<T> result = new HashSet<T>();\r
84         //Class<T> tClass = (Class<T>)result.getClass().getTypeParameters()[0].getBounds()[0];\r
85         IStructuredSelection ss = (IStructuredSelection) sel;\r
86         for (Object o : ss.toArray())\r
87             //if (tClass.isAssignableFrom(o.getClass()))\r
88             result.add((T)o);\r
89         return result;\r
90     }\r
91 \r
92 \r
93     @SuppressWarnings("unchecked")\r
94     public static <T> T convertSingleSelection(ISelection sel)\r
95     {\r
96         if (sel.isEmpty() || (!(sel instanceof IStructuredSelection)))\r
97             return null;\r
98         IStructuredSelection ss = (IStructuredSelection) sel;\r
99         return (ss.size() == 1) ? (T) ss.getFirstElement() : null;\r
100     }\r
101 \r
102     /**\r
103      * This method converts selection sel to collection of instances of T\r
104      * @param <T> type of interest\r
105      * @param sel selection\r
106      * @return list\r
107      */\r
108     @SuppressWarnings("unchecked")\r
109     public static <T> List<T> filterSelection(ISelection sel, Class<T> assignableFrom)\r
110     {\r
111         if (sel == null || sel.isEmpty() || (!(sel instanceof IStructuredSelection)))\r
112             return Collections.emptyList();\r
113 \r
114         List<T> result = new ArrayList<T>();\r
115         IStructuredSelection ss = (IStructuredSelection) sel;\r
116         for (Object o : ss.toArray()) {\r
117             if (o != null && assignableFrom.isAssignableFrom(o.getClass())) {\r
118                 result.add((T)o);\r
119             } else if (o instanceof IAdaptable) {\r
120                 T t = (T) ((IAdaptable) o).getAdapter(assignableFrom);\r
121                 if (t != null)\r
122                     result.add(t);\r
123             }\r
124         }\r
125         return result;\r
126     }\r
127 \r
128     /**\r
129      * This method reads selection sel for all instances of T.\r
130      * \r
131      * @param <T> type of interest\r
132      * @param sel selection\r
133      * @return all instances of T in sel\r
134      */\r
135     public static <T> Set<T> filterSetSelection(Object sel, Class<T> assignableFrom)\r
136     {\r
137         if (sel instanceof IStructuredSelection) {\r
138             IStructuredSelection ss = (IStructuredSelection) sel;\r
139             if (!ss.isEmpty())\r
140                 return filterCollection(ss.toList(), assignableFrom, new HashSet<T>());\r
141         } else if (sel instanceof Collection<?>) {\r
142             Collection<?> c = (Collection<?>) sel;\r
143             if (!c.isEmpty())\r
144                 return filterCollection(c, assignableFrom, new HashSet<T>());\r
145         }\r
146 \r
147         return Collections.emptySet();\r
148     }\r
149 \r
150     @SuppressWarnings("unchecked")\r
151     public static <T> T filterSingleSelection(Object sel, Class<T> assignableTo)\r
152     {\r
153         if (sel == null) {\r
154                 return null;\r
155         } else if (sel instanceof IStructuredSelection) {\r
156             IStructuredSelection ss = (IStructuredSelection) sel;\r
157             if (ss.size() == 1)\r
158                 return tryAdapt(ss.getFirstElement(), assignableTo);\r
159         } else if (sel instanceof Collection<?>) {\r
160             Collection<?> c = (Collection<?>) sel;\r
161             if (c.size() == 1)\r
162                 return tryAdapt(c.iterator().next(), assignableTo);\r
163         } else if (sel.getClass().isArray()) {\r
164                 Object[] os = (Object[])sel;\r
165                 Object result = null;\r
166                 for(Object o : os) {\r
167                         Object r = tryAdapt(o, assignableTo);\r
168                         if(r != null) {\r
169                                 if(result != null) return null;\r
170                                 result = r;\r
171                         }\r
172                 }\r
173                 return (T)result;\r
174         }\r
175         return null;\r
176     }\r
177 \r
178     public static <T> T findFirstAdaptable(Object sel, Class<T> assignableTo)\r
179     {\r
180         if (sel instanceof IStructuredSelection) {\r
181             IStructuredSelection ss = (IStructuredSelection) sel;\r
182             if (!ss.isEmpty())\r
183                 return findFirstAdaptable_(ss.toList(), assignableTo);\r
184         } else if (sel instanceof Collection<?>) {\r
185             Collection<?> c = (Collection<?>) sel;\r
186             if (!c.isEmpty())\r
187                 return findFirstAdaptable_(c, assignableTo);\r
188         }\r
189         return null;\r
190     }\r
191 \r
192     private static <T> T findFirstAdaptable_(Collection<?> objects, Class<T> assignableTo) {\r
193         for (Object o : objects) {\r
194             T t = tryAdapt(o, assignableTo);\r
195             if (t != null)\r
196                 return t;\r
197         }\r
198         return null;\r
199     }\r
200 \r
201     private static <T, C extends Collection<T>> C filterCollection(Collection<?> objects, Class<T> assignableTo, C result) {\r
202         for (Object o : objects) {\r
203             T t = tryAdapt(o, assignableTo);\r
204             if (t != null)\r
205                 result.add(t);\r
206         }\r
207         return result;\r
208     }\r
209 \r
210     @SuppressWarnings("unchecked")\r
211     private static <T> T tryAdapt(Object o, Class<T> assignableTo) {\r
212         if (o != null && assignableTo.isAssignableFrom(o.getClass())) {\r
213             return (T) o;\r
214         } else if (o instanceof IAdaptable) {\r
215             T t = (T) ((IAdaptable) o).getAdapter(assignableTo);\r
216             if (t != null) {\r
217                 return t;\r
218             }\r
219         }\r
220         return null;\r
221     }\r
222 \r
223     /**\r
224      * Try to extract a single object that is an instance of the specified class\r
225      * out of the provided selection object.\r
226      * \r
227      * This tries everything even remotely plausible - and then some.\r
228      * \r
229      * This method works as follows:\r
230      * <ul>\r
231      * <li>Supported input selection objects: IStructuredSelection and\r
232      * Collection&lt;?&gt;, IHintContext, IAdaptable, direct instances of the\r
233      * requested class</li>\r
234      * <li>Selection elements are assumed to be either instances of\r
235      * IHintContext, IAdaptable or direct instances of the requested class</li>\r
236      * <li>If an IHintContext is found, the result object is searched within it\r
237      * with the specified key.</li>\r
238      * <li>Searching involves testing whether the hint value is an instance of\r
239      * clazz or adaptable to it through {@link IAdaptable}.</li>\r
240      * </ul>\r
241      * \r
242      * \r
243      * \r
244      * @param selection\r
245      * @param key\r
246      * @param clazz desired class of the objects to look for in the selection\r
247      * @return a single objects matching the search criteria. If there are no or\r
248      *         several matches, <code>null</code> is returned\r
249      */\r
250     @SuppressWarnings("unchecked")\r
251     public static <T> T getSinglePossibleKey(Object object, Key key, Class<T> clazz) {\r
252         return getSinglePossibleKey(object, key, clazz, true);\r
253     }\r
254 \r
255     /**\r
256      * Try to extract a single object that is an instance of the specified class\r
257      * out of the provided selection object.\r
258      * \r
259      * This tries everything even remotely plausible - and then some.\r
260      * \r
261      * This method works as follows:\r
262      * <ul>\r
263      * <li>Supported input selection objects: IStructuredSelection and\r
264      * Collection&lt;?&gt;, IHintContext, IAdaptable, direct instances of the\r
265      * requested class</li>\r
266      * <li>Selection elements are assumed to be either instances of\r
267      * IHintContext, IAdaptable or direct instances of the requested class</li>\r
268      * <li>If an IHintContext is found, the result object is searched within it\r
269      * with the specified key.</li>\r
270      * <li>Searching involves testing whether the hint value is an instance of\r
271      * clazz or adaptable to it through {@link IAdaptable}.</li>\r
272      * </ul>\r
273      * \r
274      * \r
275      * \r
276      * @param selection\r
277      * @param key\r
278      * @param clazz desired class of the objects to look for in the selection\r
279      * @return a single objects matching the search criteria. If there are no or\r
280      *         several matches, <code>null</code> is returned\r
281      */\r
282     @SuppressWarnings("unchecked")\r
283     public static <T> T getSinglePossibleKey(Object object, Key key, Class<T> clazz, boolean allowDirectMatch) {\r
284         if (object == null)\r
285             return null;\r
286 \r
287         // Direct match is returned without key analysis\r
288         if (allowDirectMatch && clazz.isInstance(object))\r
289             return (T) object;\r
290 \r
291         if (object instanceof ISelection) {\r
292             ISelection sel = (ISelection) object;\r
293 \r
294             if (sel.isEmpty() || (!(sel instanceof IStructuredSelection)))\r
295                 return null;\r
296 \r
297             IStructuredSelection ss = (IStructuredSelection) sel;\r
298             if (ss.size() != 1)\r
299                 return null;\r
300 \r
301             // The result must now be in the first and only element\r
302             return getSinglePossibleKey(ss.getFirstElement(), key, clazz, allowDirectMatch);\r
303         }\r
304 \r
305         if (object instanceof Collection<?>) {\r
306             Collection<?> c = (Collection<?>) object;\r
307             if (c.size() != 1)\r
308                 return null;\r
309 \r
310             return getSinglePossibleKey(c.iterator().next(), key, clazz, allowDirectMatch);\r
311         }\r
312 \r
313         // If there was no success so far now we must take the Key into account\r
314         if (object instanceof IHintContext) {\r
315             IHintContext context = (IHintContext) object;\r
316             Object hint = context.getHint(key);\r
317 \r
318             // We had a hint context and a matching key was not available - now try single hint - TODO: this somewhat questionable\r
319             if (hint == null) {\r
320                 // NOTE: Removed because of a bug:\r
321                 // https://www.simantics.org/redmine/issues/3061\r
322                 // Hope this doesn't break existing code.\r
323                 Map<Key, Object> hints = context.getHints();\r
324                 // There are multiple hints, thats it, there is no result\r
325                 if(hints.size() != 1) return null;\r
326                 hint = hints.values().iterator().next();\r
327 \r
328                 T t = getSinglePossibleKey(hint, key, clazz);\r
329 //                if (t != null) {\r
330 //                    System.out.println("******************** GEEZ: " + t);\r
331 //                    new Exception().printStackTrace();\r
332 //                }\r
333                 return t;\r
334 //                return null;\r
335             }\r
336 \r
337             if (clazz.isInstance(hint)) {\r
338                 return (T) hint;\r
339             } else if (hint instanceof IAdaptable) {\r
340                 T adapter = (T) ((IAdaptable) hint).getAdapter(clazz);\r
341                 if (adapter != null)\r
342                     return adapter;\r
343             } else {\r
344                 return getSinglePossibleKey(hint, key, clazz);\r
345             }\r
346         }\r
347 \r
348         if (object instanceof IAdaptable)\r
349             return getSinglePossibleKey(((IAdaptable) object).getAdapter(IHintContext.class), key, clazz, allowDirectMatch);\r
350 \r
351         return null;\r
352 \r
353     }\r
354 \r
355     /**\r
356      * Try to extract the possible objects that are instances of the specified\r
357      * class out of the provided selection object.\r
358      * \r
359      * This method works as follows:\r
360      * <ul>\r
361      * <li>Supported input selection objects: IStructuredSelection and\r
362      * Collection&lt;?&gt;</li>\r
363      * <li>Selection elements are assumed to be IHintContext instances</li>\r
364      * <li>The result objects are searched for in the IHintContexts with the\r
365      * specified key.</li>\r
366      * <li>Searching involves testing whether the hint value is an instance of\r
367      * clazz or adaptable to it through {@link IAdaptable}.</li>\r
368      * </ul>\r
369      * \r
370      * All objects that pass through this filter are returned as the result.\r
371      * \r
372      * @param selection\r
373      * @param key\r
374      * @param clazz desired class of the objects to look for in the selection\r
375      * @return list of the criteria matching elements in the selection or empty\r
376      *         list if nothing suitable was found\r
377      */\r
378     public static <T> List<T> getPossibleKeys(Object selection, Key key, Class<T> clazz) {\r
379         if (selection == null)\r
380             return Collections.emptyList();\r
381 \r
382         if (selection instanceof IStructuredSelection) {\r
383             IStructuredSelection ss = (IStructuredSelection) selection;\r
384             if (ss.isEmpty())\r
385                 return Collections.emptyList();\r
386             return extractPossibleKeys(ss.toList(), key, clazz);\r
387         }\r
388 \r
389         if (selection instanceof Collection<?>) {\r
390             Collection<?> c = (Collection<?>) selection;\r
391             return extractPossibleKeys(c, key, clazz);\r
392         }\r
393 \r
394         if (selection.getClass().isArray()) {\r
395                 Object[] c = (Object[]) selection;\r
396             return extractPossibleKeys(c, key, clazz);\r
397         }\r
398 \r
399         return extractPossibleKeys(Collections.singleton(selection), key, clazz);\r
400     }\r
401 \r
402     @SuppressWarnings("unchecked")\r
403     private static <T> List<T> extractPossibleKeys(Collection<?> objects, Key key, Class<T> clazz) {\r
404         if (objects.isEmpty())\r
405             return Collections.emptyList();\r
406 \r
407         ArrayList<T> result = new ArrayList<T>(objects.size());\r
408         for (Object o : objects) {\r
409             if (o instanceof IHintContext) {\r
410                 IHintContext context = (IHintContext) o;\r
411                 Object object = context.getHint(key);\r
412                 if (object != null) {\r
413                     if (clazz.isInstance(object)) {\r
414                         result.add((T) object);\r
415                     } else if (object instanceof IAdaptable) {\r
416                         Object adapter = ((IAdaptable) object).getAdapter(clazz);\r
417                         if (adapter != null)\r
418                             result.add((T) adapter);\r
419                     }\r
420                 }\r
421             }\r
422         }\r
423         return result;\r
424     }\r
425 \r
426     @SuppressWarnings("unchecked")\r
427     private static <T> List<T> extractPossibleKeys(Object[] objects, Key key, Class<T> clazz) {\r
428         if (objects.length==0)\r
429             return Collections.emptyList();\r
430 \r
431         ArrayList<T> result = new ArrayList<T>(objects.length);\r
432         for (Object o : objects) {\r
433             if (o instanceof IHintContext) {\r
434                 IHintContext context = (IHintContext) o;\r
435                 Object object = context.getHint(key);\r
436                 if (object != null) {\r
437                     if (clazz.isInstance(object)) {\r
438                         result.add((T) object);\r
439                     } else if (object instanceof IAdaptable) {\r
440                         Object adapter = ((IAdaptable) object).getAdapter(clazz);\r
441                         if (adapter != null)\r
442                             result.add((T) adapter);\r
443                     }\r
444                 }\r
445             }\r
446         }\r
447         return result;\r
448     }\r
449 \r
450     /**\r
451      * This method creates selection from set of objects\r
452      * \r
453      * @param objects objects\r
454      * @return selection\r
455      */\r
456     public static ISelection createSelection(Object ... objects)\r
457     {\r
458         return new StructuredSelection(objects);\r
459     }\r
460 \r
461 \r
462     /**\r
463      * A testcase.\r
464      * \r
465      * @param args\r
466      */\r
467     public static void main(String[] args) {\r
468         Object o1 = new Integer(9);\r
469         Object o2 = new HashMap<Object, Object>();\r
470         Object o3 = new Long(1);\r
471 \r
472         ISelection s1 = createSelection(o1, o2, o3);\r
473 \r
474         List<Number> f1 = convertSelection(s1);\r
475         List<Number> f2 = filterSelection(s1, Number.class);\r
476 \r
477         System.out.println(f1.toString());\r
478         System.out.println(f2.toString());\r
479 \r
480         Assert.isTrue(f2.contains(o1) && f2.contains(o3) && !f2.contains(o2));\r
481         Assert.isTrue(f1.contains(o1) && f1.contains(o3) && !f1.contains(o2));\r
482     }\r
483 \r
484 }\r