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