FSF GCC merge 02/23/03
[official-gcc.git] / libjava / java / beans / Introspector.java
blobac9e36721e4a00c95d9c828052392a764489f3b4
1 /* java.beans.Introspector
2 Copyright (C) 1998, 2002 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.*;
42 import java.util.*;
43 import java.lang.reflect.*;
44 import gnu.java.lang.*;
46 /**
47 * Introspector is the class that does the bulk of the
48 * design-time work in Java Beans. Every class must have
49 * a BeanInfo in order for an RAD tool to use it; but, as
50 * promised, you don't have to write the BeanInfo class
51 * yourself if you don't want to. All you have to do is
52 * call getBeanInfo() in the Introspector and it will use
53 * standard JavaBeans-defined method signatures to
54 * determine the information about your class.<P>
56 * Don't worry about it too much, though: you can provide
57 * JavaBeans with as much customized information as you
58 * want, or as little as you want, using the BeanInfo
59 * interface (see BeanInfo for details).<P>
61 * <STRONG>Order of Operations</STRONG><P>
63 * When you call getBeanInfo(class c), the Introspector
64 * first searches for BeanInfo class to see if you
65 * provided any explicit information. It searches for a
66 * class named <bean class name>BeanInfo in different
67 * packages, first searching the bean class's package
68 * and then moving on to search the beanInfoSearchPath.<P>
70 * If it does not find a BeanInfo class, it acts as though
71 * it had found a BeanInfo class returning null from all
72 * methods (meaning it should discover everything through
73 * Introspection). If it does, then it takes the
74 * information it finds in the BeanInfo class to be
75 * canonical (that is, the information speaks for its
76 * class as well as all superclasses).<P>
78 * When it has introspected the class, calls
79 * getBeanInfo(c.getSuperclass) and adds that information
80 * to the information it has, not adding to any information
81 * it already has that is canonical.<P>
83 * <STRONG>Introspection Design Patterns</STRONG><P>
85 * When the Introspector goes in to read the class, it
86 * follows a well-defined order in order to not leave any
87 * methods unaccounted for. Its job is to step over all
88 * of the public methods in a class and determine whether
89 * they are part of a property, an event, or a method (in
90 * that order).
93 * <STRONG>Properties:</STRONG><P>
95 * <OL>
96 * <LI>If there is a <CODE>public boolean isXXX()</CODE>
97 * method, then XXX is a read-only boolean property.
98 * <CODE>boolean getXXX()</CODE> may be supplied in
99 * addition to this method, although isXXX() is the
100 * one that will be used in this case and getXXX()
101 * will be ignored. If there is a
102 * <CODE>public void setXXX(boolean)</CODE> method,
103 * it is part of this group and makes it a read-write
104 * property.</LI>
105 * <LI>If there is a
106 * <CODE>public &lt;type&gt; getXXX(int)</CODE>
107 * method, then XXX is a read-only indexed property of
108 * type &lt;type&gt;. If there is a
109 * <CODE>public void setXXX(int,&lt;type&gt;)</CODE>
110 * method, then it is a read-write indexed property of
111 * type &lt;type&gt;. There may also be a
112 * <CODE>public &lt;type&gt;[] getXXX()</CODE> and a
113 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
114 * method as well.</CODE></LI>
115 * <LI>If there is a
116 * <CODE>public void setXXX(int,&lt;type&gt;)</CODE>
117 * method, then it is a write-only indexed property of
118 * type &lt;type&gt;. There may also be a
119 * <CODE>public &lt;type&gt;[] getXXX()</CODE> and a
120 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
121 * method as well.</CODE></LI>
122 * <LI>If there is a
123 * <CODE>public &lt;type&gt; getXXX()</CODE> method,
124 * then XXX is a read-only property of type
125 * &lt;type&gt;. If there is a
126 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
127 * method, then it will be used for the property and
128 * the property will be considered read-write.</LI>
129 * <LI>If there is a
130 * <CODE>public void setXXX(&lt;type&gt;)</CODE>
131 * method, then as long as XXX is not already used as
132 * the name of a property, XXX is assumed to be a
133 * write-only property of type &lt;type&gt;.</LI>
134 * <LI>In all of the above cases, if the setXXX() method
135 * throws <CODE>PropertyVetoException</CODE>, then the
136 * property in question is assumed to be constrained.
137 * No properties are ever assumed to be bound
138 * (<STRONG>Spec Note:</STRONG> this is not in the
139 * spec, it just makes sense). See PropertyDescriptor
140 * for a description of bound and constrained
141 * properties.</LI>
142 * </OL>
144 * <STRONG>Events:</STRONG><P>
146 * If there is a pair of methods,
147 * <CODE>public void addXXX(&lt;type&gt;)</CODE> and
148 * <CODE>public void removeXXX(&lt;type&gt;)</CODE>, where
149 * &lt;type&gt; is a descendant of
150 * <CODE>java.util.EventListener</CODE>, then the pair of
151 * methods imply that this Bean will fire events to
152 * listeners of type &lt;type&gt;.<P>
154 * If the addXXX() method throws
155 * <CODE>java.util.TooManyListenersException</CODE>, then
156 * the event set is assumed to be <EM>unicast</EM>. See
157 * EventSetDescriptor for a discussion of unicast event
158 * sets.<P>
160 * <STRONG>Spec Note:</STRONG> the spec seems to say that
161 * the listener type's classname must be equal to the XXX
162 * part of addXXX() and removeXXX(), but that is not the
163 * case in Sun's implementation, so I am assuming it is
164 * not the case in general.<P>
166 * <STRONG>Methods:</STRONG><P>
168 * Any public methods (including those which were used
169 * for Properties or Events) are used as Methods.
171 * @author John Keiser
172 * @since JDK1.1
173 * @see java.beans.BeanInfo
175 public class Introspector {
177 public static final int USE_ALL_BEANINFO = 1;
178 public static final int IGNORE_IMMEDIATE_BEANINFO = 2;
179 public static final int IGNORE_ALL_BEANINFO = 3;
181 static String[] beanInfoSearchPath = {"gnu.java.beans.info"};
182 static Hashtable beanInfoCache = new Hashtable();
184 private Introspector() {}
186 /**
187 * Get the BeanInfo for class <CODE>beanClass</CODE>,
188 * first by looking for explicit information, next by
189 * using standard design patterns to determine
190 * information about the class.
192 * @param beanClass the class to get BeanInfo about.
193 * @return the BeanInfo object representing the class.
195 public static BeanInfo getBeanInfo(Class beanClass)
196 throws IntrospectionException
198 BeanInfo cachedInfo;
199 synchronized(beanClass)
201 cachedInfo = (BeanInfo)beanInfoCache.get(beanClass);
202 if(cachedInfo != null)
204 return cachedInfo;
206 cachedInfo = getBeanInfo(beanClass,null);
207 beanInfoCache.put(beanClass,cachedInfo);
208 return cachedInfo;
213 * Flush all of the Introspector's internal caches.
215 * @since 1.2
217 public static void flushCaches()
219 beanInfoCache.clear();
223 * Flush the Introspector's internal cached information for a given
224 * class.
226 * @param clz the class to be flushed.
227 * @throws NullPointerException if clz is null.
228 * @since 1.2
230 public static void flushFromCaches(Class clz)
232 synchronized (clz)
234 beanInfoCache.remove(clz);
238 /**
239 * Get the BeanInfo for class <CODE>beanClass</CODE>,
240 * first by looking for explicit information, next by
241 * using standard design patterns to determine
242 * information about the class. It crawls up the
243 * inheritance tree until it hits <CODE>topClass</CODE>.
245 * @param beanClass the Bean class.
246 * @param stopClass the class to stop at.
247 * @return the BeanInfo object representing the class.
249 public static BeanInfo getBeanInfo(Class beanClass, Class stopClass)
250 throws IntrospectionException
252 ExplicitInfo explicit = new ExplicitInfo(beanClass,stopClass);
254 IntrospectionIncubator ii = new IntrospectionIncubator();
255 ii.setPropertyStopClass(explicit.propertyStopClass);
256 ii.setEventStopClass(explicit.eventStopClass);
257 ii.setMethodStopClass(explicit.methodStopClass);
258 ii.addMethods(beanClass.getMethods());
260 BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo();
261 PropertyDescriptor[] p = explicit.explicitPropertyDescriptors;
262 if(p!=null)
264 for(int i=0;i<p.length;i++)
266 if(!currentInfo.hasProperty(p[i]))
268 currentInfo.addProperty(p[i]);
271 if(explicit.defaultProperty != -1)
273 currentInfo.setDefaultPropertyName(p[explicit.defaultProperty].getName());
276 EventSetDescriptor[] e = explicit.explicitEventSetDescriptors;
277 if(e!=null)
279 for(int i=0;i<e.length;i++)
281 if(!currentInfo.hasEvent(e[i]))
283 currentInfo.addEvent(e[i]);
286 if(explicit.defaultEvent != -1)
288 currentInfo.setDefaultEventName(e[explicit.defaultEvent].getName());
291 MethodDescriptor[] m = explicit.explicitMethodDescriptors;
292 if(m!=null)
294 for(int i=0;i<m.length;i++)
296 if(!currentInfo.hasMethod(m[i]))
298 currentInfo.addMethod(m[i]);
303 if(explicit.explicitBeanDescriptor != null)
305 currentInfo.setBeanDescriptor(new BeanDescriptor(beanClass,explicit.explicitBeanDescriptor.getCustomizerClass()));
307 else
309 currentInfo.setBeanDescriptor(new BeanDescriptor(beanClass,null));
312 currentInfo.setAdditionalBeanInfo(explicit.explicitBeanInfo);
313 currentInfo.setIcons(explicit.im);
315 return currentInfo.getBeanInfo();
318 /**
319 * Get the search path for BeanInfo classes.
321 * @return the BeanInfo search path.
323 public static String[] getBeanInfoSearchPath()
325 return beanInfoSearchPath;
328 /**
329 * Set the search path for BeanInfo classes.
330 * @param beanInfoSearchPath the new BeanInfo search
331 * path.
333 public static void setBeanInfoSearchPath(String[] beanInfoSearchPath)
335 Introspector.beanInfoSearchPath = beanInfoSearchPath;
338 /**
339 * A helper method to convert a name to standard Java
340 * naming conventions: anything with two capitals as the
341 * first two letters remains the same, otherwise the
342 * first letter is decapitalized. URL = URL, I = i,
343 * MyMethod = myMethod.
345 * @param name the name to decapitalize.
346 * @return the decapitalized name.
348 public static String decapitalize(String name)
350 try
352 if(!Character.isUpperCase(name.charAt(0)))
354 return name;
356 else
358 try
360 if(Character.isUpperCase(name.charAt(1)))
362 return name;
364 else
366 char[] c = name.toCharArray();
367 c[0] = Character.toLowerCase(c[0]);
368 return new String(c);
371 catch(StringIndexOutOfBoundsException E)
373 char[] c = new char[1];
374 c[0] = Character.toLowerCase(name.charAt(0));
375 return new String(c);
379 catch(StringIndexOutOfBoundsException E)
381 return name;
383 catch(NullPointerException E)
385 return null;
389 static BeanInfo copyBeanInfo(BeanInfo b)
391 java.awt.Image[] icons = new java.awt.Image[4];
392 for(int i=1;i<=4;i++)
394 icons[i-1] = b.getIcon(i);
396 return new ExplicitBeanInfo(b.getBeanDescriptor(),
397 b.getAdditionalBeanInfo(),
398 b.getPropertyDescriptors(),
399 b.getDefaultPropertyIndex(),
400 b.getEventSetDescriptors(),
401 b.getDefaultEventIndex(),
402 b.getMethodDescriptors(),icons);
406 class ExplicitInfo
408 BeanDescriptor explicitBeanDescriptor;
409 BeanInfo[] explicitBeanInfo;
411 PropertyDescriptor[] explicitPropertyDescriptors;
412 EventSetDescriptor[] explicitEventSetDescriptors;
413 MethodDescriptor[] explicitMethodDescriptors;
415 int defaultProperty;
416 int defaultEvent;
418 java.awt.Image[] im = new java.awt.Image[4];
420 Class propertyStopClass;
421 Class eventStopClass;
422 Class methodStopClass;
424 ExplicitInfo(Class beanClass, Class stopClass)
426 while(beanClass != null && !beanClass.equals(stopClass))
428 BeanInfo explicit = findExplicitBeanInfo(beanClass);
429 if(explicit != null)
431 if(explicitBeanDescriptor == null)
433 explicitBeanDescriptor = explicit.getBeanDescriptor();
435 if(explicitBeanInfo == null)
437 explicitBeanInfo = explicit.getAdditionalBeanInfo();
439 if(explicitPropertyDescriptors == null)
441 if(explicit.getPropertyDescriptors() != null)
443 explicitPropertyDescriptors = explicit.getPropertyDescriptors();
444 defaultProperty = explicit.getDefaultPropertyIndex();
445 propertyStopClass = beanClass;
448 if(explicitEventSetDescriptors == null)
450 if(explicit.getEventSetDescriptors() != null)
452 explicitEventSetDescriptors = explicit.getEventSetDescriptors();
453 defaultEvent = explicit.getDefaultEventIndex();
454 eventStopClass = beanClass;
457 if(explicitMethodDescriptors == null)
459 if(explicit.getMethodDescriptors() != null)
461 explicitMethodDescriptors = explicit.getMethodDescriptors();
462 methodStopClass = beanClass;
465 if(im[0] == null && im[1] == null
466 && im[2] == null && im[3] == null)
468 im[0] = explicit.getIcon(0);
469 im[1] = explicit.getIcon(1);
470 im[2] = explicit.getIcon(2);
471 im[3] = explicit.getIcon(3);
474 beanClass = beanClass.getSuperclass();
476 if(propertyStopClass == null)
478 propertyStopClass = stopClass;
480 if(eventStopClass == null)
482 eventStopClass = stopClass;
484 if(methodStopClass == null)
486 methodStopClass = stopClass;
490 static Hashtable explicitBeanInfos = new Hashtable();
491 static Vector emptyBeanInfos = new Vector();
493 static BeanInfo findExplicitBeanInfo(Class beanClass)
495 BeanInfo retval = (BeanInfo)explicitBeanInfos.get(beanClass);
496 if(retval != null)
498 return retval;
500 else if(emptyBeanInfos.indexOf(beanClass) != -1)
502 return null;
504 else
506 retval = reallyFindExplicitBeanInfo(beanClass);
507 if(retval != null)
509 explicitBeanInfos.put(beanClass,retval);
511 else
513 emptyBeanInfos.addElement(beanClass);
515 return retval;
519 static BeanInfo reallyFindExplicitBeanInfo(Class beanClass)
521 try
523 try
525 return (BeanInfo)Class.forName(beanClass.getName()+"BeanInfo").newInstance();
527 catch(ClassNotFoundException E)
530 String newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo";
531 for(int i=0;i<Introspector.beanInfoSearchPath.length;i++)
533 try
535 if(Introspector.beanInfoSearchPath[i].equals(""))
537 return (BeanInfo)Class.forName(newName).newInstance();
539 else
541 return (BeanInfo)Class.forName(Introspector.beanInfoSearchPath[i] + "." + newName).newInstance();
544 catch(ClassNotFoundException E)
549 catch(IllegalAccessException E)
552 catch(InstantiationException E)
555 return null;