2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / beans / Introspector.java
blob078f98eb37cde31e7ab09b515d5be649274ff2c5
1 /* java.beans.Introspector
2 Copyright (C) 1998, 2002, 2003 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.beans;
41 import gnu.java.beans.BeanInfoEmbryo;
42 import gnu.java.beans.ExplicitBeanInfo;
43 import gnu.java.beans.IntrospectionIncubator;
44 import java.util.Hashtable;
45 import java.util.Vector;
46 import gnu.java.lang.ClassHelper;
48 /**
49 * Introspector is the class that does the bulk of the
50 * design-time work in Java Beans. Every class must have
51 * a BeanInfo in order for an RAD tool to use it; but, as
52 * promised, you don't have to write the BeanInfo class
53 * yourself if you don't want to. All you have to do is
54 * call getBeanInfo() in the Introspector and it will use
55 * standard JavaBeans-defined method signatures to
56 * determine the information about your class.<P>
58 * Don't worry about it too much, though: you can provide
59 * JavaBeans with as much customized information as you
60 * want, or as little as you want, using the BeanInfo
61 * interface (see BeanInfo for details).<P>
63 * <STRONG>Order of Operations</STRONG><P>
65 * When you call getBeanInfo(class c), the Introspector
66 * first searches for BeanInfo class to see if you
67 * provided any explicit information. It searches for a
68 * class named <bean class name>BeanInfo in different
69 * packages, first searching the bean class's package
70 * and then moving on to search the beanInfoSearchPath.<P>
72 * If it does not find a BeanInfo class, it acts as though
73 * it had found a BeanInfo class returning null from all
74 * methods (meaning it should discover everything through
75 * Introspection). If it does, then it takes the
76 * information it finds in the BeanInfo class to be
77 * canonical (that is, the information speaks for its
78 * class as well as all superclasses).<P>
80 * When it has introspected the class, calls
81 * getBeanInfo(c.getSuperclass) and adds that information
82 * to the information it has, not adding to any information
83 * it already has that is canonical.<P>
85 * <STRONG>Introspection Design Patterns</STRONG><P>
87 * When the Introspector goes in to read the class, it
88 * follows a well-defined order in order to not leave any
89 * methods unaccounted for. Its job is to step over all
90 * of the public methods in a class and determine whether
91 * they are part of a property, an event, or a method (in
92 * that order).
95 * <STRONG>Properties:</STRONG><P>
97 * <OL>
98 * <LI>If there is a <CODE>public boolean isXXX()</CODE>
99 * method, then XXX is a read-only boolean property.
100 * <CODE>boolean getXXX()</CODE> may be supplied in
101 * addition to this method, although isXXX() is the
102 * one that will be used in this case and getXXX()
103 * will be ignored. If there is a
104 * <CODE>public void setXXX(boolean)</CODE> method,
105 * it is part of this group and makes it a read-write
106 * property.</LI>
107 * <LI>If there is a
108 * <CODE>public &lt;type&gt; getXXX(int)</CODE>
109 * method, then XXX is a read-only indexed property of
110 * type &lt;type&gt;. If there is a
111 * <CODE>public void setXXX(int,&lt;type&gt;)</CODE>
112 * method, then it is a read-write indexed property of
113 * type &lt;type&gt;. There may also be a
114 * <CODE>public &lt;type&gt;[] getXXX()</CODE> and a
115 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
116 * method as well.</CODE></LI>
117 * <LI>If there is a
118 * <CODE>public void setXXX(int,&lt;type&gt;)</CODE>
119 * method, then it is a write-only indexed property of
120 * type &lt;type&gt;. There may also be a
121 * <CODE>public &lt;type&gt;[] getXXX()</CODE> and a
122 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
123 * method as well.</CODE></LI>
124 * <LI>If there is a
125 * <CODE>public &lt;type&gt; getXXX()</CODE> method,
126 * then XXX is a read-only property of type
127 * &lt;type&gt;. If there is a
128 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
129 * method, then it will be used for the property and
130 * the property will be considered read-write.</LI>
131 * <LI>If there is a
132 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
133 * method, then as long as XXX is not already used as
134 * the name of a property, XXX is assumed to be a
135 * write-only property of type &lt;type&gt;.</LI>
136 * <LI>In all of the above cases, if the setXXX() method
137 * throws <CODE>PropertyVetoException</CODE>, then the
138 * property in question is assumed to be constrained.
139 * No properties are ever assumed to be bound
140 * (<STRONG>Spec Note:</STRONG> this is not in the
141 * spec, it just makes sense). See PropertyDescriptor
142 * for a description of bound and constrained
143 * properties.</LI>
144 * </OL>
146 * <STRONG>Events:</STRONG><P>
148 * If there is a pair of methods,
149 * <CODE>public void addXXX(&lt;type&gt;)</CODE> and
150 * <CODE>public void removeXXX(&lt;type&gt;)</CODE>, where
151 * &lt;type&gt; is a descendant of
152 * <CODE>java.util.EventListener</CODE>, then the pair of
153 * methods imply that this Bean will fire events to
154 * listeners of type &lt;type&gt;.<P>
156 * If the addXXX() method throws
157 * <CODE>java.util.TooManyListenersException</CODE>, then
158 * the event set is assumed to be <EM>unicast</EM>. See
159 * EventSetDescriptor for a discussion of unicast event
160 * sets.<P>
162 * <STRONG>Spec Note:</STRONG> the spec seems to say that
163 * the listener type's classname must be equal to the XXX
164 * part of addXXX() and removeXXX(), but that is not the
165 * case in Sun's implementation, so I am assuming it is
166 * not the case in general.<P>
168 * <STRONG>Methods:</STRONG><P>
170 * Any public methods (including those which were used
171 * for Properties or Events) are used as Methods.
173 * @author John Keiser
174 * @since JDK1.1
175 * @see java.beans.BeanInfo
177 public class Introspector {
179 public static final int USE_ALL_BEANINFO = 1;
180 public static final int IGNORE_IMMEDIATE_BEANINFO = 2;
181 public static final int IGNORE_ALL_BEANINFO = 3;
183 static String[] beanInfoSearchPath = {"gnu.java.beans.info"};
184 static Hashtable beanInfoCache = new Hashtable();
186 private Introspector() {}
188 /**
189 * Get the BeanInfo for class <CODE>beanClass</CODE>,
190 * first by looking for explicit information, next by
191 * using standard design patterns to determine
192 * information about the class.
194 * @param beanClass the class to get BeanInfo about.
195 * @return the BeanInfo object representing the class.
197 public static BeanInfo getBeanInfo(Class beanClass)
198 throws IntrospectionException
200 BeanInfo cachedInfo;
201 synchronized(beanClass)
203 cachedInfo = (BeanInfo)beanInfoCache.get(beanClass);
204 if(cachedInfo != null)
206 return cachedInfo;
208 cachedInfo = getBeanInfo(beanClass,null);
209 beanInfoCache.put(beanClass,cachedInfo);
210 return cachedInfo;
215 * Flush all of the Introspector's internal caches.
217 * @since 1.2
219 public static void flushCaches()
221 beanInfoCache.clear();
225 * Flush the Introspector's internal cached information for a given
226 * class.
228 * @param clz the class to be flushed.
229 * @throws NullPointerException if clz is null.
230 * @since 1.2
232 public static void flushFromCaches(Class clz)
234 synchronized (clz)
236 beanInfoCache.remove(clz);
240 /**
241 * Get the BeanInfo for class <CODE>beanClass</CODE>,
242 * first by looking for explicit information, next by
243 * using standard design patterns to determine
244 * information about the class. It crawls up the
245 * inheritance tree until it hits <CODE>topClass</CODE>.
247 * @param beanClass the Bean class.
248 * @param stopClass the class to stop at.
249 * @return the BeanInfo object representing the class.
251 public static BeanInfo getBeanInfo(Class beanClass, Class stopClass)
252 throws IntrospectionException
254 ExplicitInfo explicit = new ExplicitInfo(beanClass,stopClass);
256 IntrospectionIncubator ii = new IntrospectionIncubator();
257 ii.setPropertyStopClass(explicit.propertyStopClass);
258 ii.setEventStopClass(explicit.eventStopClass);
259 ii.setMethodStopClass(explicit.methodStopClass);
260 ii.addMethods(beanClass.getMethods());
262 BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo();
263 PropertyDescriptor[] p = explicit.explicitPropertyDescriptors;
264 if(p!=null)
266 for(int i=0;i<p.length;i++)
268 if(!currentInfo.hasProperty(p[i]))
270 currentInfo.addProperty(p[i]);
273 if(explicit.defaultProperty != -1)
275 currentInfo.setDefaultPropertyName(p[explicit.defaultProperty].getName());
278 EventSetDescriptor[] e = explicit.explicitEventSetDescriptors;
279 if(e!=null)
281 for(int i=0;i<e.length;i++)
283 if(!currentInfo.hasEvent(e[i]))
285 currentInfo.addEvent(e[i]);
288 if(explicit.defaultEvent != -1)
290 currentInfo.setDefaultEventName(e[explicit.defaultEvent].getName());
293 MethodDescriptor[] m = explicit.explicitMethodDescriptors;
294 if(m!=null)
296 for(int i=0;i<m.length;i++)
298 if(!currentInfo.hasMethod(m[i]))
300 currentInfo.addMethod(m[i]);
305 if(explicit.explicitBeanDescriptor != null)
307 currentInfo.setBeanDescriptor(new BeanDescriptor(beanClass,explicit.explicitBeanDescriptor.getCustomizerClass()));
309 else
311 currentInfo.setBeanDescriptor(new BeanDescriptor(beanClass,null));
314 currentInfo.setAdditionalBeanInfo(explicit.explicitBeanInfo);
315 currentInfo.setIcons(explicit.im);
317 return currentInfo.getBeanInfo();
320 /**
321 * Get the search path for BeanInfo classes.
323 * @return the BeanInfo search path.
325 public static String[] getBeanInfoSearchPath()
327 return beanInfoSearchPath;
330 /**
331 * Set the search path for BeanInfo classes.
332 * @param beanInfoSearchPath the new BeanInfo search
333 * path.
335 public static void setBeanInfoSearchPath(String[] beanInfoSearchPath)
337 Introspector.beanInfoSearchPath = beanInfoSearchPath;
340 /**
341 * A helper method to convert a name to standard Java
342 * naming conventions: anything with two capitals as the
343 * first two letters remains the same, otherwise the
344 * first letter is decapitalized. URL = URL, I = i,
345 * MyMethod = myMethod.
347 * @param name the name to decapitalize.
348 * @return the decapitalized name.
350 public static String decapitalize(String name)
352 try
354 if(!Character.isUpperCase(name.charAt(0)))
356 return name;
358 else
360 try
362 if(Character.isUpperCase(name.charAt(1)))
364 return name;
366 else
368 char[] c = name.toCharArray();
369 c[0] = Character.toLowerCase(c[0]);
370 return new String(c);
373 catch(StringIndexOutOfBoundsException E)
375 char[] c = new char[1];
376 c[0] = Character.toLowerCase(name.charAt(0));
377 return new String(c);
381 catch(StringIndexOutOfBoundsException E)
383 return name;
385 catch(NullPointerException E)
387 return null;
391 static BeanInfo copyBeanInfo(BeanInfo b)
393 java.awt.Image[] icons = new java.awt.Image[4];
394 for(int i=1;i<=4;i++)
396 icons[i-1] = b.getIcon(i);
398 return new ExplicitBeanInfo(b.getBeanDescriptor(),
399 b.getAdditionalBeanInfo(),
400 b.getPropertyDescriptors(),
401 b.getDefaultPropertyIndex(),
402 b.getEventSetDescriptors(),
403 b.getDefaultEventIndex(),
404 b.getMethodDescriptors(),icons);
408 class ExplicitInfo
410 BeanDescriptor explicitBeanDescriptor;
411 BeanInfo[] explicitBeanInfo;
413 PropertyDescriptor[] explicitPropertyDescriptors;
414 EventSetDescriptor[] explicitEventSetDescriptors;
415 MethodDescriptor[] explicitMethodDescriptors;
417 int defaultProperty;
418 int defaultEvent;
420 java.awt.Image[] im = new java.awt.Image[4];
422 Class propertyStopClass;
423 Class eventStopClass;
424 Class methodStopClass;
426 ExplicitInfo(Class beanClass, Class stopClass)
428 while(beanClass != null && !beanClass.equals(stopClass))
430 BeanInfo explicit = findExplicitBeanInfo(beanClass);
431 if(explicit != null)
433 if(explicitBeanDescriptor == null)
435 explicitBeanDescriptor = explicit.getBeanDescriptor();
437 if(explicitBeanInfo == null)
439 explicitBeanInfo = explicit.getAdditionalBeanInfo();
441 if(explicitPropertyDescriptors == null)
443 if(explicit.getPropertyDescriptors() != null)
445 explicitPropertyDescriptors = explicit.getPropertyDescriptors();
446 defaultProperty = explicit.getDefaultPropertyIndex();
447 propertyStopClass = beanClass;
450 if(explicitEventSetDescriptors == null)
452 if(explicit.getEventSetDescriptors() != null)
454 explicitEventSetDescriptors = explicit.getEventSetDescriptors();
455 defaultEvent = explicit.getDefaultEventIndex();
456 eventStopClass = beanClass;
459 if(explicitMethodDescriptors == null)
461 if(explicit.getMethodDescriptors() != null)
463 explicitMethodDescriptors = explicit.getMethodDescriptors();
464 methodStopClass = beanClass;
467 if(im[0] == null && im[1] == null
468 && im[2] == null && im[3] == null)
470 im[0] = explicit.getIcon(0);
471 im[1] = explicit.getIcon(1);
472 im[2] = explicit.getIcon(2);
473 im[3] = explicit.getIcon(3);
476 beanClass = beanClass.getSuperclass();
478 if(propertyStopClass == null)
480 propertyStopClass = stopClass;
482 if(eventStopClass == null)
484 eventStopClass = stopClass;
486 if(methodStopClass == null)
488 methodStopClass = stopClass;
492 static Hashtable explicitBeanInfos = new Hashtable();
493 static Vector emptyBeanInfos = new Vector();
495 static BeanInfo findExplicitBeanInfo(Class beanClass)
497 BeanInfo retval = (BeanInfo)explicitBeanInfos.get(beanClass);
498 if(retval != null)
500 return retval;
502 else if(emptyBeanInfos.indexOf(beanClass) != -1)
504 return null;
506 else
508 retval = reallyFindExplicitBeanInfo(beanClass);
509 if(retval != null)
511 explicitBeanInfos.put(beanClass,retval);
513 else
515 emptyBeanInfos.addElement(beanClass);
517 return retval;
521 static BeanInfo reallyFindExplicitBeanInfo(Class beanClass)
523 ClassLoader beanClassLoader = beanClass.getClassLoader();
524 BeanInfo beanInfo;
526 beanInfo = getBeanInfo(beanClassLoader, beanClass.getName() + "BeanInfo");
527 if (beanInfo == null)
529 String newName;
530 newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo";
532 for(int i = 0; i < Introspector.beanInfoSearchPath.length; i++)
534 if (Introspector.beanInfoSearchPath[i].equals(""))
535 beanInfo = getBeanInfo(beanClassLoader, newName);
536 else
537 beanInfo = getBeanInfo(beanClassLoader,
538 Introspector.beanInfoSearchPath[i] + "."
539 + newName);
541 if (beanInfo != null)
542 return beanInfo;
546 return beanInfo;
550 * Returns an instance of the given class name when it can be loaded
551 * through the given class loader, or null otherwise.
553 private static BeanInfo getBeanInfo(ClassLoader cl, String infoName)
557 return (BeanInfo) Class.forName(infoName, true, cl).newInstance();
559 catch (ClassNotFoundException cnfe)
561 return null;
563 catch (IllegalAccessException iae)
565 return null;
567 catch (InstantiationException ie)
569 return null;