/* * $Id: BeanUtils.java,v 1.2 2006/09/25 08:52:36 acaproni Exp $ * * $Date: 2006/09/25 08:52:36 $ * $Revision: 1.2 $ * $Author: acaproni $ * * Copyright CERN, All Rights Reserved. */ package cern.gp.beans; import java.beans.Introspector; import java.beans.PropertyChangeListener; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import org.openide.util.WeakListener; /** * A few utility methods for Beans. * * @author Lionel Mestre * @version $REvision: $ $Date: 2006/09/25 08:52:36 $ */ public class BeanUtils { private BeanUtils() { } /** * Generates a unique name for the bean using the same convention as the * implementation of toString() in Object, which is : * "bean class name"+@+"hash code of the bean in hexadecimal" * @param bean the bean the name has to be generated from * @return a unique name for the bean */ public static final String generateUniqueBeanName(Object bean) { return bean.getClass().getName() + "@" + Integer.toHexString(bean.hashCode()); } /** * Registers a set of paths in the <code>PropertyEditor</code> search path of * the <code>java.beans.PropertyEditorManager</code>. * <p> * Use this method when you have <code>PropertyEditor</code> named following the convention * <i>Typename</i>Editor that are not located in the same package as <i>Typename</i>. * For a given java type <i>a.b.X</i>, the <code>java.beans.PropertyEditorManager</code> will * first check if there is an editor registered for the type, if not it will look for the editor * <i>a.b.XEditor</i> and if not found it will look in the search path for the class <i>XEditor</i>. * You can use this method to register the path where <i>XEditor</i> is. For instance, if the editor is located in * <code>a.c.d.XEditor</code> you would pass in parameter to this method <code>new String[] {"a.c.d"}</code>. * </p><p> * Note that paths are not added if they are already present. * </p> * @param pathsToRegister the array of paths to register in the search path of the PropertyEditorManager. */ public static final void registerEditorSearchPaths(String[] pathsToRegister) { java.beans.PropertyEditorManager.setEditorSearchPath( computePaths(java.beans.PropertyEditorManager.getEditorSearchPath(), pathsToRegister)); } /** * Registers a set of paths in the <code>BeanInfo</code> search path of * the <code>java.beans.Introspector</code>. * <p> * Use this method when you have <code>BeanInfo</code> named following the convention <i>BeanName</i>BeanInfo that are * not located in the same package as <i>BeanName</i>. For a given java bean <i>a.b.X</i>, the <code>java.beans. * Introspector</code> will first check if there is a BeanInfo <i>a.b.XBeanInfo</i> and if not found it will look in * the search path for the class <i>XBeanInfo</i>. You can use this method to register the path where <i>XBeanInfo</i> * is. For instance, if the BeanInfo is located in <code>a.c.d.XBeanInfo</code> you would pass in parameter * to this method <code>new String [] {"a.c.d"}</code>. * </p><p> * Note that paths are not added if they are already present. * </p> * @param pathsToRegister the array of paths to register in the search path of the Introspector. */ public static final void registerBeanInfoSearchPaths(String[] pathsToRegister) { Introspector.setBeanInfoSearchPath(computePaths(Introspector.getBeanInfoSearchPath(), pathsToRegister)); } private static final String[] computePaths(String[] existingPaths, String[] pathsToRegister) { if (pathsToRegister == null || pathsToRegister.length == 0) { return existingPaths; } if (existingPaths == null || existingPaths.length == 0) { return pathsToRegister; } String[] newPaths = (String[]) pathsToRegister.clone(); int n = 0; for (int i = 0; i < newPaths.length; i++) { if (checkDoesBeanInfoPathExist(existingPaths, newPaths[i])) { newPaths[i] = null; } else { n++; } } if (n == 0) return existingPaths; int m = existingPaths.length; String[] resultingPaths = new String[n + m]; System.arraycopy(existingPaths, 0, resultingPaths, 0, m); for (int i = 0; i < newPaths.length; i++) { if (newPaths[i] != null) { resultingPaths[m] = newPaths[i]; m++; } } return resultingPaths; } private static final boolean checkDoesBeanInfoPathExist(String[] paths, String path) { int n = paths.length; for (int i = 0; i < n; i++) { if (paths[i].equals(path)) return true; } return false; } /** * This is a utility method to help in loading icon images. It takes the pathname of a resource file * associated with the a given class and loads an image object from that file. Typically images will be GIFs. * <p> * The pathname should be relative to the given class and be contained in the classpath. * For instance if the related class is in <code>cern.gp.beans</code> and the icon is stored in * <code>cern/gp/beans/images/MyIcon.gif</code> the pathname to give would be * <code>images/MyIcon.gif</code>. * </p> * @param imagePathname A pathname relative to the directory holding the class file of * the given relatedClass. For example, <code>images/MyIcon.gif</code>. * @param relatedClass the class the image is related with. * @return an image object. May be null if the load failed. */ public static final java.awt.Image loadImage(final String imagePathname, final Class relatedClass) { try { java.awt.image.ImageProducer ip = (java.awt.image.ImageProducer) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { java.net.URL url = relatedClass.getResource(imagePathname); if (url == null) return null; try { return url.getContent(); } catch (java.io.IOException ioe) { return null; } } }); if (ip == null) return null; java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit(); return tk.createImage(ip); } catch (Exception ex) { return null; } } /** * Finds the first public superclass of the given bean. * Should not introspect on a private class, because then the method objects * used for the property descriptors will not be callable without an * IllegalAccessException, even if overriding a public method from a public superclass. * @param bean the bean to find the public class from * @return the first public superclass, possibly the class of the given bean */ public static final Class findTargetClass(Object bean) { Class clazz = bean.getClass(); while (!Modifier.isPublic(clazz.getModifiers()) && !hasExplicitBeanInfo(clazz)) { clazz = clazz.getSuperclass(); if (clazz == null) clazz = Object.class; // in case it was an interface } return clazz; } /** * Checks whether there is an explicit bean info for given class. * @param clazz the class to test * @return true if explicit bean info exists */ public static final boolean hasExplicitBeanInfo(Class clazz) { String className = clazz.getName(); int indx = className.lastIndexOf('.'); className = className.substring(indx + 1); String[] paths = Introspector.getBeanInfoSearchPath(); for (int i = 0; i < paths.length; i++) { String s = paths[i] + '.' + className + "BeanInfo"; try { // test if such class exists Class.forName(s); return true; } catch (ClassNotFoundException ex) { // OK, this is normal. } } return false; } /** * Add a weak PropertyChangeListener to an object, using reflection to look up the addPropertyChangeListener method * @param listener the listener to add * @param source the object to which a PCL shall be added */ public static void addWeakListenerByReflection(PropertyChangeListener listener, Object source) { try { PropertyChangeListener pcl = WeakListener.propertyChange(listener, source); Method addPCL = source.getClass().getMethod( "addPropertyChangeListener", new Class[] { java.beans.PropertyChangeListener.class }); addPCL.invoke(source, new Object[] { pcl }); } catch (Exception ex) { // [PENDING] should use logging API here System.err.println("Warning: unable to add a property change listener to object " + source); //ex.printStackTrace(); } } }