View Javadoc
1   /*
2   
3       dsh-identify  Lightweight components for identifiable beans.
4       Copyright (c) 2003-2019 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.identify;
25  
26  import java.awt.ComponentOrientation;
27  import java.beans.BeanInfo;
28  import java.beans.BeanDescriptor;
29  import java.beans.Introspector;
30  import java.beans.PropertyDescriptor;
31  import java.beans.IntrospectionException;
32  
33  import java.lang.reflect.Method;
34  import java.lang.reflect.InvocationTargetException;
35  
36  import java.net.URL;
37  
38  import javax.swing.JLabel;
39  import javax.swing.LookAndFeel;
40  import javax.swing.UIManager;
41  
42  import org.dishevelled.iconbundle.IconBundle;
43  import org.dishevelled.iconbundle.IconTextDirection;
44  
45  import org.dishevelled.iconbundle.impl.PNGIconBundle;
46  
47  /**
48   * Provides static utility methods for determining
49   * the name property value and icon bundle for a bean.
50   *
51   * @author  Michael Heuer
52   */
53  public final class IdentifyUtils
54  {
55      /** Name strategy. */
56      private NameStrategy nameStrategy;
57  
58      /** Icon bundle strategy. */
59      private IconBundleStrategy iconBundleStrategy;
60  
61      /** Default icon bundle. */
62      private IconBundle defaultIconBundle;
63  
64      /** Private static instance of IdentifyUtils. */
65      private static IdentifyUtils instance;
66  
67      /** GTK look and feel class name. */
68      private static final String GTK_LOOK_AND_FEEL_CLASS_NAME = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
69  
70      /** JDK <= 1.5 MacOSX look and feel class name. */
71      private static final String JDK15_MAC_OSX_LOOK_AND_FEEL_CLASS_NAME = "apple.laf.AquaLookAndFeel";
72  
73      /** JDK >= 1.6 MacOSX look and feel class name. */
74      private static final String JDK16_MAC_OSX_LOOK_AND_FEEL_CLASS_NAME = "com.apple.laf.AquaLookAndFeel";
75  
76      /** Windows look and feel class name. */
77      private static final String WINDOWS_LOOK_AND_FEEL_CLASS_NAME = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
78  
79  
80      /**
81       * Private no-arg constructor.
82       */
83      private IdentifyUtils()
84      {
85          nameStrategy = new DefaultNameStrategy();
86          iconBundleStrategy = new DefaultIconBundleStrategy();
87  
88          URL url = getClass().getResource("default.png");
89          defaultIconBundle = new PNGIconBundle(url);
90      }
91  
92  
93      /**
94       * Return the static instance of IdentifyUtils.
95       *
96       * @return the static instance of IdentifyUtils
97       */
98      public static IdentifyUtils getInstance()
99      {
100         if (instance == null)
101         {
102             instance = new IdentifyUtils();
103         }
104         return instance;
105     }
106 
107     /**
108      * Return a name for the specified bean using the set name
109      * strategy.  Return the String <code>"null"</code> if
110      * <code>bean</code> is null.
111      *
112      * @see #getNameStrategy
113      *
114      * @param bean bean
115      * @return a name for the specified bean
116      */
117     public static String getNameFor(final Object bean)
118     {
119         return getInstance().getNameStrategy().getNameFor(bean);
120     }
121 
122     /**
123      * Return an icon bundle for the specified bean using the
124      * set icon bundle strategy.  Return <code>null</code> if
125      * <code>bean</code> is null, and the default icon bundle
126      * if an icon bundle cannot otherwise be found.
127      *
128      * @see #getIconBundleStrategy
129      * @see #getDefaultIconBundle
130      *
131      * @param bean bean
132      * @return an icon bundle for the specified bean
133      */
134     public static IconBundle getIconBundleFor(final Object bean)
135     {
136         return getInstance().getIconBundleStrategy().getIconBundleFor(bean);
137     }
138 
139     /**
140      * Return true if the current look and feel is the GTK look and feel.
141      *
142      * @see #GTK_LOOK_AND_FEEL_CLASS_NAME
143      * @return true if the current look and feel is the GTK look and feel
144      */
145     public static boolean isGTKLookAndFeel()
146     {
147         LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
148         if (lookAndFeel == null)
149         {
150             return false;
151         }
152         return GTK_LOOK_AND_FEEL_CLASS_NAME.equals(lookAndFeel.getClass().getName());
153     }
154 
155     /**
156      * Return true if the current look and feel is the MacOSX look and feel.
157      *
158      * @see #JDK15_MAC_OSX_LOOK_AND_FEEL_CLASS_NAME
159      * @see #JDK16_MAC_OSX_LOOK_AND_FEEL_CLASS_NAME
160      * @return true if the current look and feel is the MacOSX look and feel
161      */
162     public static boolean isMacOSXLookAndFeel()
163     {
164         LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
165         if (lookAndFeel == null)
166         {
167             return false;
168         }
169         return JDK15_MAC_OSX_LOOK_AND_FEEL_CLASS_NAME.equals(lookAndFeel.getClass().getName())
170             || JDK16_MAC_OSX_LOOK_AND_FEEL_CLASS_NAME.equals(lookAndFeel.getClass().getName());
171     }
172 
173     /**
174      * Return true if the current look and feel is the Windows look and feel.
175      *
176      * @see #WINDOWS_LOOK_AND_FEEL_CLASS_NAME
177      * @return true if the current look and feel is the Windows look and feel
178      */
179     public static boolean isWindowsLookAndFeel()
180     {
181         LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
182         if (lookAndFeel == null)
183         {
184             return false;
185         }
186         return WINDOWS_LOOK_AND_FEEL_CLASS_NAME.equals(lookAndFeel.getClass().getName());
187     }
188 
189     /**
190      * Return the strategy used to determine the value for
191      * a name property on a bean.
192      *
193      * @return the name strategy
194      */
195     public NameStrategy getNameStrategy()
196     {
197         return nameStrategy;
198     }
199 
200     /**
201      * Set the strategy used to determine the value for
202      * a name property on a bean to <code>nameStrategy</code>.
203      *
204      * @param nameStrategy name strategy, must not be null
205      */
206     public void setNameStrategy(final NameStrategy nameStrategy)
207     {
208         if (nameStrategy == null)
209         {
210             throw new IllegalArgumentException("nameStrategy must not be null");
211         }
212         this.nameStrategy = nameStrategy;
213     }
214 
215     /**
216      * Return the strategy used to find an icon bundle for
217      * a bean.
218      *
219      * @return the icon bundle strategy
220      */
221     public IconBundleStrategy getIconBundleStrategy()
222     {
223         return iconBundleStrategy;
224     }
225 
226     /**
227      * Set the strategy used to find an icon bundle for a bean
228      * to <code>iconBundleStrategy</code>.
229      *
230      * @param iconBundleStrategy icon bundle strategy, must not
231      *    be null
232      */
233     public void setIconBundleStrategy(final IconBundleStrategy iconBundleStrategy)
234     {
235         if (iconBundleStrategy == null)
236         {
237             throw new IllegalArgumentException("iconBundleStrategy must not be null");
238         }
239         this.iconBundleStrategy = iconBundleStrategy;
240     }
241 
242     /**
243      * Return the icon bundle to use as a default icon bundle,
244      * in the case the icon bundle strategy is unable to find an
245      * icon bundle for a bean.
246      *
247      * @return the icon bundle to use as a default icon bundle
248      */
249     public IconBundle getDefaultIconBundle()
250     {
251         return defaultIconBundle;
252     }
253 
254     /**
255      * Set the default icon bundle to <code>defaultIconBundle</code>.
256      *
257      * @param defaultIconBundle the default icon bundle, must
258      *    not be null
259      */
260     public void setDefaultIconBundle(final IconBundle defaultIconBundle)
261     {
262         if (defaultIconBundle == null)
263         {
264             throw new IllegalArgumentException("defaultIconBundle must not be null");
265         }
266         this.defaultIconBundle = defaultIconBundle;
267     }
268 
269 
270     //
271     //  strategies
272 
273     /**
274      * Strategy used to determine the value for a name
275      * property on a bean.
276      */
277     interface NameStrategy
278     {
279 
280         /**
281          * Return a name for the specified bean.  Return
282          * the String <code>"null"</code> if
283          * <code>bean</code> is null.
284          *
285          * @param bean bean
286          * @return a name for the specified bean
287          */
288         String getNameFor(Object bean);
289     }
290 
291     /**
292      * Strategy used to find an icon bundle for a bean.
293      */
294     interface IconBundleStrategy
295     {
296 
297         /**
298          * Return an icon bundle for the specified bean.
299          * Return <code>null</code> if <code>bean</code>
300          * is null, and the default icon bundle if an icon bundle
301          * cannot otherwise be found.
302          *
303          * @see #getDefaultIconBundle
304          *
305          * @param bean bean
306          * @return an icon bundle for the specified bean
307          */
308         IconBundle getIconBundleFor(Object bean);
309     }
310 
311     /**
312      * Default name strategy.  Returns the string <code>"null"</code>
313      * if the specified bean is null.  Otherwise
314      * <ul>
315      * <li>checks if the bean is instanceof Identifiable</li>
316      * <li>checks if the bean's BeanInfo's BeanDescriptor is instanceof IdentifiableBeanDescriptor</li>
317      * <li>finally, returns <code>bean.toString()</code></li>
318      *</ul>
319      */
320     private class DefaultNameStrategy
321         implements NameStrategy
322     {
323 
324         @Override
325         public String getNameFor(final Object bean)
326         {
327             if (bean == null)
328             {
329                 return "null";
330             }
331             if (bean instanceof Identifiable)
332             {
333                 return ((Identifiable) bean).getName();
334             }
335 
336             try
337             {
338                 BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
339                 BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
340 
341                 if (beanDescriptor instanceof IdentifiableBeanDescriptor)
342                 {
343                     int namePropertyIndex = ((IdentifiableBeanDescriptor) beanDescriptor).getNamePropertyIndex();
344                     if (namePropertyIndex != -1)
345                     {
346                         PropertyDescriptor namePropertyDescriptor = beanInfo.getPropertyDescriptors()[namePropertyIndex];
347                         Method readMethod = namePropertyDescriptor.getReadMethod();
348                         String name = (String) readMethod.invoke(bean, new Object[] {});
349                         return name;
350                     }
351                 }
352             }
353             catch (IntrospectionException ie)
354             {
355                 // empty
356             }
357             catch (IllegalAccessException iae)
358             {
359                 // empty
360             }
361             catch (InvocationTargetException ite)
362             {
363                 // empty
364             }
365             return bean.toString();
366         }
367     }
368 
369     /**
370      * Default icon bundle strategy.  Returns <code>null</code> if
371      * the specified bean is null.  Otherwise
372      * <ul>
373      * <li>checks if the bean is instanceof Identifiable</li>
374      * <li>checks if the bean's BeanInfo's BeanDescriptor is instanceof IdentifiableBeanDescriptor</li>
375      * <li>finally, returns the default icon bundle</li>
376      * </ul>
377      *
378      * @see #getDefaultBeanInfo
379      */
380     private class DefaultIconBundleStrategy
381         implements IconBundleStrategy
382     {
383 
384         @Override
385         public IconBundle getIconBundleFor(final Object bean)
386         {
387             if (bean == null)
388             {
389                 return null;
390             }
391             if (bean instanceof Identifiable)
392             {
393                 return ((Identifiable) bean).getIconBundle();
394             }
395 
396             try
397             {
398                 BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
399                 BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
400 
401                 if (beanDescriptor instanceof IdentifiableBeanDescriptor)
402                 {
403                     return ((IdentifiableBeanDescriptor) beanDescriptor).getIconBundle();
404                 }
405             }
406             catch (IntrospectionException ie)
407             {
408                 // empty
409             }
410             return defaultIconBundle;
411         }
412     }
413 
414     /**
415      * Determine a text direction from the component orientation of
416      * the specified label.
417      *
418      * @param label label
419      * @return a text direction for the component orientation of
420      *    the specified label
421      */
422     static IconTextDirection determineTextDirection(final JLabel label)
423     {
424         ComponentOrientation componentOrientation = label.getComponentOrientation();
425         return componentOrientation.isLeftToRight() ? IconTextDirection.LEFT_TO_RIGHT : IconTextDirection.RIGHT_TO_LEFT;
426     }
427 }