diff options
author | Tom Tromey <tromey@gcc.gnu.org> | 2000-05-19 17:55:34 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2000-05-19 17:55:34 +0000 |
commit | 6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef (patch) | |
tree | 88cf0d32aea197ea8e8198e1206b04c820308615 /libjava/java/beans/Introspector.java | |
parent | 021c89ed68c151c45021fccf1bb5338ee817314c (diff) | |
download | gcc-6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef.zip gcc-6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef.tar.gz gcc-6c80c45e3010bfe992b41dd8800d2c4b65e0d5ef.tar.bz2 |
Jumbo patch:
* Imported beans and serialization
* Updated IA-64 port
* Miscellaneous bug fixes
From-SVN: r34028
Diffstat (limited to 'libjava/java/beans/Introspector.java')
-rw-r--r-- | libjava/java/beans/Introspector.java | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/libjava/java/beans/Introspector.java b/libjava/java/beans/Introspector.java new file mode 100644 index 0000000..a191971 --- /dev/null +++ b/libjava/java/beans/Introspector.java @@ -0,0 +1,427 @@ +/* java.beans.Introspector + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.beans; + +import gnu.java.beans.*; +import java.util.*; +import java.lang.reflect.*; +import gnu.java.lang.*; + +/** + ** Introspector is the class that does the bulk of the + ** design-time work in Java Beans. Every class must have + ** a BeanInfo in order for an RAD tool to use it; but, as + ** promised, you don't have to write the BeanInfo class + ** yourself if you don't want to. All you have to do is + ** call getBeanInfo() in the Introspector and it will use + ** standard JavaBeans-defined method signatures to + ** determine the information about your class.<P> + ** + ** Don't worry about it too much, though: you can provide + ** JavaBeans with as much customized information as you + ** want, or as little as you want, using the BeanInfo + ** interface (see BeanInfo for details).<P> + ** + ** <STRONG>Order of Operations</STRONG><P> + ** + ** When you call getBeanInfo(class c), the Introspector + ** first searches for BeanInfo class to see if you + ** provided any explicit information. It searches for a + ** class named <bean class name>BeanInfo in different + ** packages, first searching the bean class's package + ** and then moving on to search the beanInfoSearchPath.<P> + ** + ** If it does not find a BeanInfo class, it acts as though + ** it had found a BeanInfo class returning null from all + ** methods (meaning it should discover everything through + ** Introspection). If it does, then it takes the + ** information it finds in the BeanInfo class to be + ** canonical (that is, the information speaks for its + ** class as well as all superclasses).<P> + ** + ** When it has introspected the class, calls + ** getBeanInfo(c.getSuperclass) and adds that information + ** to the information it has, not adding to any information + ** it already has that is canonical.<P> + ** + ** <STRONG>Introspection Design Patterns</STRONG><P> + ** + ** When the Introspector goes in to read the class, it + ** follows a well-defined order in order to not leave any + ** methods unaccounted for. Its job is to step over all + ** of the public methods in a class and determine whether + ** they are part of a property, an event, or a method (in + ** that order). + ** + ** + ** <STRONG>Properties:</STRONG><P> + ** + ** <OL> + ** <LI>If there is a <CODE>public boolean isXXX()</CODE> + ** method, then XXX is a read-only boolean property. + ** <CODE>boolean getXXX()</CODE> may be supplied in + ** addition to this method, although isXXX() is the + ** one that will be used in this case and getXXX() + ** will be ignored. If there is a + ** <CODE>public void setXXX(boolean)</CODE> method, + ** it is part of this group and makes it a read-write + ** property.</LI> + ** <LI>If there is a + ** <CODE>public <type> getXXX(int)</CODE> + ** method, then XXX is a read-only indexed property of + ** type <type>. If there is a + ** <CODE>public void setXXX(int,<type>)</CODE> + ** method, then it is a read-write indexed property of + ** type <type>. There may also be a + ** <CODE>public <type>[] getXXX()</CODE> and a + ** <CODE>public void setXXX(<type>)</CODE> + ** method as well.</CODE></LI> + ** <LI>If there is a + ** <CODE>public void setXXX(int,<type>)</CODE> + ** method, then it is a write-only indexed property of + ** type <type>. There may also be a + ** <CODE>public <type>[] getXXX()</CODE> and a + ** <CODE>public void setXXX(<type>)</CODE> + ** method as well.</CODE></LI> + ** <LI>If there is a + ** <CODE>public <type> getXXX()</CODE> method, + ** then XXX is a read-only property of type + ** <type>. If there is a + ** <CODE>public void setXXX(<type>)</CODE> + ** method, then it will be used for the property and + ** the property will be considered read-write.</LI> + ** <LI>If there is a + ** <CODE>public void setXXX(<type>)</CODE> + ** method, then as long as XXX is not already used as + ** the name of a property, XXX is assumed to be a + ** write-only property of type <type>.</LI> + ** <LI>In all of the above cases, if the setXXX() method + ** throws <CODE>PropertyVetoException</CODE>, then the + ** property in question is assumed to be constrained. + ** No properties are ever assumed to be bound + ** (<STRONG>Spec Note:</STRONG> this is not in the + ** spec, it just makes sense). See PropertyDescriptor + ** for a description of bound and constrained + ** properties.</LI> + ** </OL> + ** + ** <STRONG>Events:</STRONG><P> + ** + ** If there is a pair of methods, + ** <CODE>public void addXXX(<type>)</CODE> and + ** <CODE>public void removeXXX(<type>)</CODE>, where + ** <type> is a descendant of + ** <CODE>java.util.EventListener</CODE>, then the pair of + ** methods imply that this Bean will fire events to + ** listeners of type <type>.<P> + ** + ** If the addXXX() method throws + ** <CODE>java.util.TooManyListenersException</CODE>, then + ** the event set is assumed to be <EM>unicast</EM>. See + ** EventSetDescriptor for a discussion of unicast event + ** sets.<P> + ** + ** <STRONG>Spec Note:</STRONG> the spec seems to say that + ** the listener type's classname must be equal to the XXX + ** part of addXXX() and removeXXX(), but that is not the + ** case in Sun's implementation, so I am assuming it is + ** not the case in general.<P> + ** + ** <STRONG>Methods:</STRONG><P> + ** + ** Any public methods (including those which were used + ** for Properties or Events) are used as Methods. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 29 Jul 1998 + ** @see java.beans.BeanInfo + **/ + +public class Introspector { + static String[] beanInfoSearchPath = {"gnu.java.beans.info", "sun.beans.infos"}; + static Hashtable beanInfoCache = new Hashtable(); + + private Introspector() {} + + /** Get the BeanInfo for class <CODE>beanClass</CODE>, + ** first by looking for explicit information, next by + ** using standard design patterns to determine + ** information about the class. + ** @param beanClass the class to get BeanInfo about. + ** @return the BeanInfo object representing the class. + **/ + public static BeanInfo getBeanInfo(Class beanClass) throws IntrospectionException { + BeanInfo cachedInfo; + synchronized(beanClass) { + cachedInfo = (BeanInfo)beanInfoCache.get(beanClass); + if(cachedInfo != null) { + return cachedInfo; + } + cachedInfo = getBeanInfo(beanClass,null); + beanInfoCache.put(beanClass,cachedInfo); + return cachedInfo; + } + } + + /** Get the BeanInfo for class <CODE>beanClass</CODE>, + ** first by looking for explicit information, next by + ** using standard design patterns to determine + ** information about the class. It crawls up the + ** inheritance tree until it hits <CODE>topClass</CODE>. + ** @param beanClass the Bean class. + ** @param stopClass the class to stop at. + ** @return the BeanInfo object representing the class. + **/ + public static BeanInfo getBeanInfo(Class beanClass, Class stopClass) throws IntrospectionException { + ExplicitInfo explicit = new ExplicitInfo(beanClass,stopClass); + + IntrospectionIncubator ii = new IntrospectionIncubator(); + ii.setPropertyStopClass(explicit.propertyStopClass); + ii.setEventStopClass(explicit.eventStopClass); + ii.setMethodStopClass(explicit.methodStopClass); + ii.addMethods(beanClass.getMethods()); + + BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo(); + PropertyDescriptor[] p = explicit.explicitPropertyDescriptors; + if(p!=null) { + for(int i=0;i<p.length;i++) { + if(!currentInfo.hasProperty(p[i])) { + currentInfo.addProperty(p[i]); + } + } + if(explicit.defaultProperty != -1) { + currentInfo.setDefaultPropertyName(p[explicit.defaultProperty].getName()); + } + } + EventSetDescriptor[] e = explicit.explicitEventSetDescriptors; + if(e!=null) { + for(int i=0;i<e.length;i++) { + if(!currentInfo.hasEvent(e[i])) { + currentInfo.addEvent(e[i]); + } + } + if(explicit.defaultEvent != -1) { + currentInfo.setDefaultEventName(e[explicit.defaultEvent].getName()); + } + } + MethodDescriptor[] m = explicit.explicitMethodDescriptors; + if(m!=null) { + for(int i=0;i<m.length;i++) { + if(!currentInfo.hasMethod(m[i])) { + currentInfo.addMethod(m[i]); + } + } + } + + if(explicit.explicitBeanDescriptor != null) { + currentInfo.setBeanDescriptor(new BeanDescriptor(beanClass,explicit.explicitBeanDescriptor.getCustomizerClass())); + } else { + currentInfo.setBeanDescriptor(new BeanDescriptor(beanClass,null)); + } + + currentInfo.setAdditionalBeanInfo(explicit.explicitBeanInfo); + currentInfo.setIcons(explicit.im); + + return currentInfo.getBeanInfo(); + } + + /** Get the search path for BeanInfo classes. + ** @return the BeanInfo search path. + **/ + public static String[] getBeanInfoSearchPath() { + return beanInfoSearchPath; + } + + /** Set the search path for BeanInfo classes. + ** @param beanInfoSearchPath the new BeanInfo search + ** path. + **/ + public static void setBeanInfoSearchPath(String[] beanInfoSearchPath) { + Introspector.beanInfoSearchPath = beanInfoSearchPath; + } + + /** A helper method to convert a name to standard Java + ** naming conventions: anything with two capitals as the + ** first two letters remains the same, otherwise the + ** first letter is decapitalized. URL = URL, I = i, + ** MyMethod = myMethod. + ** @param name the name to decapitalize. + ** @return the decapitalized name. + **/ + public static String decapitalize(String name) { + try { + if(!Character.isUpperCase(name.charAt(0))) { + return name; + } else { + try { + if(Character.isUpperCase(name.charAt(1))) { + return name; + } else { + char[] c = name.toCharArray(); + c[0] = Character.toLowerCase(c[0]); + return new String(c); + } + } catch(StringIndexOutOfBoundsException E) { + char[] c = new char[1]; + c[0] = Character.toLowerCase(name.charAt(0)); + return new String(c); + } + } + } catch(StringIndexOutOfBoundsException E) { + return name; + } catch(NullPointerException E) { + return null; + } + } + + static BeanInfo copyBeanInfo(BeanInfo b) { + java.awt.Image[] icons = new java.awt.Image[4]; + for(int i=1;i<=4;i++) { + icons[i-1] = b.getIcon(i); + } + return new ExplicitBeanInfo(b.getBeanDescriptor(),b.getAdditionalBeanInfo(), + b.getPropertyDescriptors(),b.getDefaultPropertyIndex(), + b.getEventSetDescriptors(),b.getDefaultEventIndex(), + b.getMethodDescriptors(),icons); + } +} + +class ExplicitInfo { + BeanDescriptor explicitBeanDescriptor; + BeanInfo[] explicitBeanInfo; + + PropertyDescriptor[] explicitPropertyDescriptors; + EventSetDescriptor[] explicitEventSetDescriptors; + MethodDescriptor[] explicitMethodDescriptors; + + int defaultProperty; + int defaultEvent; + + java.awt.Image[] im = new java.awt.Image[4]; + + Class propertyStopClass; + Class eventStopClass; + Class methodStopClass; + + ExplicitInfo(Class beanClass, Class stopClass) { + while(beanClass != null && !beanClass.equals(stopClass)) { + BeanInfo explicit = findExplicitBeanInfo(beanClass); + if(explicit != null) { + if(explicitBeanDescriptor == null) { + explicitBeanDescriptor = explicit.getBeanDescriptor(); + } + if(explicitBeanInfo == null) { + explicitBeanInfo = explicit.getAdditionalBeanInfo(); + } + if(explicitPropertyDescriptors == null) { + if(explicit.getPropertyDescriptors() != null) { + explicitPropertyDescriptors = explicit.getPropertyDescriptors(); + defaultProperty = explicit.getDefaultPropertyIndex(); + propertyStopClass = beanClass; + } + } + if(explicitEventSetDescriptors == null) { + if(explicit.getEventSetDescriptors() != null) { + explicitEventSetDescriptors = explicit.getEventSetDescriptors(); + defaultEvent = explicit.getDefaultEventIndex(); + eventStopClass = beanClass; + } + } + if(explicitMethodDescriptors == null) { + if(explicit.getMethodDescriptors() != null) { + explicitMethodDescriptors = explicit.getMethodDescriptors(); + methodStopClass = beanClass; + } + } + if(im[0] == null + && im[1] == null + && im[2] == null + && im[3] == null) { + im[0] = explicit.getIcon(0); + im[1] = explicit.getIcon(1); + im[2] = explicit.getIcon(2); + im[3] = explicit.getIcon(3); + } + } + beanClass = beanClass.getSuperclass(); + } + if(propertyStopClass == null) { + propertyStopClass = stopClass; + } + if(eventStopClass == null) { + eventStopClass = stopClass; + } + if(methodStopClass == null) { + methodStopClass = stopClass; + } + } + + static Hashtable explicitBeanInfos = new Hashtable(); + static Vector emptyBeanInfos = new Vector(); + + static BeanInfo findExplicitBeanInfo(Class beanClass) { + BeanInfo retval = (BeanInfo)explicitBeanInfos.get(beanClass); + if(retval != null) { + return retval; + } else if(emptyBeanInfos.indexOf(beanClass) != -1) { + return null; + } else { + retval = reallyFindExplicitBeanInfo(beanClass); + if(retval != null) { + explicitBeanInfos.put(beanClass,retval); + } else { + emptyBeanInfos.addElement(beanClass); + } + return retval; + } + } + + static BeanInfo reallyFindExplicitBeanInfo(Class beanClass) { + try { + try { + return (BeanInfo)Class.forName(beanClass.getName()+"BeanInfo").newInstance(); + } catch(ClassNotFoundException E) { + } + String newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo"; + for(int i=0;i<Introspector.beanInfoSearchPath.length;i++) { + try { + if(Introspector.beanInfoSearchPath[i].equals("")) { + return (BeanInfo)Class.forName(newName).newInstance(); + } else { + return (BeanInfo)Class.forName(Introspector.beanInfoSearchPath[i] + "." + newName).newInstance(); + } + } catch(ClassNotFoundException E) { + } + } + } catch(IllegalAccessException E) { + } catch(InstantiationException E) { + } + return null; + } +} |