diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/java/beans | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/java/beans')
59 files changed, 13248 insertions, 0 deletions
diff --git a/libjava/classpath/java/beans/AppletInitializer.java b/libjava/classpath/java/beans/AppletInitializer.java new file mode 100644 index 000000000..3bc2534e1 --- /dev/null +++ b/libjava/classpath/java/beans/AppletInitializer.java @@ -0,0 +1,61 @@ +/* java.beans.AppletInitializer + Copyright (C) 2001, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.applet.Applet; +import java.beans.beancontext.BeanContext; + + +/** This interface is a mechanism for the initialization of a Java + * Bean that is also an Applet. It is used by + * <code>Beans.instantiate()</code>. + * + * @author Tom Tromey (tromey@redhat.com) + * @since 1.2 + */ +public interface AppletInitializer +{ + /** Activate the applet. */ + void activate (Applet applet); + + /** This method will be called by <code>Beans.instantiate()</code> + * to associated the new Applet with its AppletContext, AppletStub, + * and Container. + */ + void initialize (Applet applet, BeanContext context); +} diff --git a/libjava/classpath/java/beans/BeanDescriptor.java b/libjava/classpath/java/beans/BeanDescriptor.java new file mode 100644 index 000000000..6795d91b3 --- /dev/null +++ b/libjava/classpath/java/beans/BeanDescriptor.java @@ -0,0 +1,89 @@ +/* java.beans.BeanDescriptor + Copyright (C) 1998, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** BeanDescriptor describes general information about a Bean, plus + ** stores the Bean's Class and it's customizer's Class.<P> + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 31 May 1998 + **/ + +public class BeanDescriptor extends FeatureDescriptor { + Class<?> beanClass; + Class<?> customizerClass; + + /** Create a new BeanDescriptor with the given beanClass and + ** no customizer class. + ** @param beanClass the class of the Bean. + **/ + public BeanDescriptor(Class<?> beanClass) { + this(beanClass,null); + } + + /** Create a new BeanDescriptor with the given bean class and + ** customizer class. + ** @param beanClass the class of the Bean. + ** @param customizerClass the class of the Bean's Customizer. + **/ + public BeanDescriptor(Class<?> beanClass, Class<?> customizerClass) { + this.beanClass = beanClass; + this.customizerClass = customizerClass; + + // Set the FeatureDescriptor programmatic name. + String name = beanClass.getName(); + int lastInd = name.lastIndexOf('.'); + if (lastInd != -1) + name = name.substring(lastInd + 1); + + setName(name); + } + + /** Get the Bean's class. **/ + public Class<?> getBeanClass() { + return beanClass; + } + + /** Get the Bean's customizer's class. **/ + public Class<?> getCustomizerClass() { + return customizerClass; + } +} diff --git a/libjava/classpath/java/beans/BeanInfo.java b/libjava/classpath/java/beans/BeanInfo.java new file mode 100644 index 000000000..3c9bf1bcd --- /dev/null +++ b/libjava/classpath/java/beans/BeanInfo.java @@ -0,0 +1,181 @@ +/* java.beans.BeanInfo + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** BeanInfo can be implemented in order to provide explicit information to the Introspector. + ** + ** When you write a BeanInfo class, you implement this interface + ** and provide explicit information by returning a non-null + ** value from the appropriate method. If you wish the + ** Introspector to determine certain information in the normal + ** way, just return null (or in the case of int methods, return + ** -1). There is a class called SimpleBeanInfo which returns + ** null from all methods, which you may extend and only + ** override the methods you wish to override.<P> + ** + ** When you have written the class, give it the name + ** <CODE><Bean Class Name>BeanInfo</CODE> and place it in + ** the same package as the Bean, or in the bean info search path + ** (see Introspector for information on search paths).<P> + ** + ** A simple note about the way the Introspector interacts with + ** BeanInfo. Introspectors look at a Bean class and determine + ** if there is a BeanInfo class with it. If there is not a + ** BeanInfo class, it will behave as if the BeanInfo class + ** provided was a SimpleBeanInfo class (i.e. it will determine + ** all information automatically).<P>If there is a BeanInfo + ** class, then any methods that do *not* return null are + ** regarded as providing definitive information about the class + ** and all of its superclasses for those information types. + ** Even if a parent BeanInfo class explicitly returns that + ** information, it will not be used. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 28 Jul 1998 + **/ + +public interface BeanInfo { + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_COLOR_16x16 = 1; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_COLOR_32x32 = 2; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_MONO_16x16 = 3; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_MONO_32x32 = 4; + + /** Get the general description of this Bean type. + ** @return the BeanDescriptor for the Bean, or null if + ** the BeanDescriptor should be obtained by + ** Introspection. + **/ + BeanDescriptor getBeanDescriptor(); + + /** Get the events this Bean type fires. + ** @return the EventDescriptors representing events this + ** Bean fires. Returns <CODE>null</CODE> if the + ** events are to be acquired by Introspection. + **/ + EventSetDescriptor[] getEventSetDescriptors(); + + /** Get the "default" event, basically the one a RAD tool + ** user is most likely to select. + ** @return the index into the getEventSetDescriptors() + ** that the user is most likely to use. Returns + ** <CODE>-1</CODE> if there is no default event. + **/ + int getDefaultEventIndex(); + + /** Get the properties (get/set method pairs) this Bean + ** type supports. + ** @return the PropertyDescriptors representing the + ** properties this Bean type supports. + ** Returns <CODE>null</CODE> if the properties + ** are to be obtained by Introspection. + **/ + PropertyDescriptor[] getPropertyDescriptors(); + + /** Get the "default" property, basically the one a RAD + ** tool user is most likely to select. + ** @return the index into the getPropertyDescriptors() + ** that the user is most likely to use. Returns + ** <CODE>-1</CODE> if there is no default event. + **/ + int getDefaultPropertyIndex(); + + /** Get the methods this Bean type supports. + ** @return the MethodDescriptors representing the + ** methods this Bean type supports. Returns + ** <CODE>null</CODE> if the methods are to be + ** obtained by Introspection. + **/ + MethodDescriptor[] getMethodDescriptors(); + + /** Get additional BeanInfos representing this Bean. + ** In this version of JavaBeans, this method is used so + ** that space and time can be saved by reading a BeanInfo + ** for each class in the hierarchy (super, super(super), + ** and so on).<P> + ** + ** The order of precedence when two pieces of BeanInfo + ** conflict (such as two PropertyDescriptors that have + ** the same name), in order from highest precedence to + ** lowest, is: + ** <OL> + ** <LI>This BeanInfo object.</LI> + ** <LI><CODE>getAdditionalBeanInfo()[getAdditionalBeanInfo().length]</CODE></LI> + ** <LI> ... </LI> + ** <LI><CODE>getAdditionalBeanInfo()[1]</CODE></LI> + ** <LI><CODE>getAdditionalBeanInfo()[0]</CODE></LI> + ** </OL><P> + ** + ** <STRONG>Spec Note:</STRONG> It is possible that + ** returning <CODE>null</CODE> from this method could + ** stop Introspection in its tracks, but it is unclear + ** from the spec whether this is the case. + ** + ** @return additional BeanInfos representing this Bean. + ** <CODE>null</CODE> may be returned (see Spec + ** Note, above). + **/ + BeanInfo[] getAdditionalBeanInfo(); + + /** Get a visual icon for this Bean. + ** A Bean does not have to support icons, and if it does + ** support icons, it does not have to support every single + ** type. Sun recommends that if you only support one + ** type, you support 16x16 color. Sun also notes that you + ** should try to use a type (like GIF) that allows for + ** transparent pixels, so that the background of the RAD + ** tool can show through.<P> + ** + ** <STRONG>Spec Note:</STRONG> If you do not support the + ** type of icon that is being asked for, but you do + ** support another type, it is unclear whether you should + ** return the other type or not. I would presume not. + ** + ** @param iconType the type of icon to get (see the + ** ICON_* constants in this class). + ** @return the icon, or null if that type of icon is + ** unsupported by this Bean. + **/ + java.awt.Image getIcon(int iconType); +} diff --git a/libjava/classpath/java/beans/Beans.java b/libjava/classpath/java/beans/Beans.java new file mode 100644 index 000000000..3066be7a5 --- /dev/null +++ b/libjava/classpath/java/beans/Beans.java @@ -0,0 +1,368 @@ +/* java.beans.Beans + Copyright (C) 1998, 1999, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import gnu.java.beans.DummyAppletStub; +import gnu.java.io.ClassLoaderObjectInputStream; + +import java.applet.Applet; +import java.beans.beancontext.BeanContext; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.URL; + +/** + * <code>Beans</code> provides some helper methods that allow the basic + * operations of Bean-ness. + * + * @author John Keiser + * @author Robert Schuster + * + * @since 1.1 + * @status updated to 1.4 + * + */ +public class Beans +{ + static boolean designTime = false; + static boolean guiAvailable = true; + + /** + * Once again, we have a java.beans class with only + * static methods that can be instantiated. When + * will the madness end? :) + */ + public Beans() + { + // Does intentionally nothing here. + } + + /** Creates a bean. + * <p>This is a convenience method that calls <code>instantiate(cl, beanName, null, null)</code>.</p> + * + * @see instantiate(ClassLoader, String, BeanContext, AppletInitializer) + * @param cl ClassLoader to be used or <code>null</code> for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate(ClassLoader cl, String beanName) + throws IOException, ClassNotFoundException + { + return instantiate(cl, beanName, null, null); + } + + /** Creates a bean. + * + * <p>This is a convenience method that calls <code>instantiate(cl, beanName, beanContext, null)</code>.</p> + * + * @see instantiate(ClassLoader, String, BeanContext, AppletInitializer) + * @param cl ClassLoader to be used or <code>null</code> for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @param beanContext Context to which the newly created Bean should be added. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate( + ClassLoader cl, + String beanName, + BeanContext beanContext) + throws IOException, ClassNotFoundException + { + return instantiate(cl, beanName, beanContext, null); + } + + /** Instantiates a bean according to Beans 1.0. + * + * <p>In Beans 1.0 the instantiation scheme is as follows:</p> + * <p>The name should be dot-separated (e.g "place.for.beans.myBean") and indicate either a + * serialized object or a class name. In the first case all dots in the name are replaced with + * slashes ('/') and ".ser" is appended ("place.for.beans.myBean" becomes "place/for/beans/myBean.ser"). + * The bean is then loaded as an application or system resource depending on whether a + * <code>ClassLoader</code> was provided.</p> + * + * <p>If no such resource exists or if it contains no bean the name is interpreted as a class name of + * which an instance is then created.</p> + * + * <p>If a <code>BeanContext</code> instance is available the created bean is added to it.</p> + * + * <p>If the created Bean is an <code>Applet</code> or subclass and an <code>AppletInitializer</code> + * instance is available the applet is initialized and afterwards activated using the initializer. Additionally + * every instantiated <code>Applet</code> bean is initialized using the {@link Applet.init} method. + * Furthermore every applet gets a default <code>AppletStub</code>. The <code>Applet</code>'s + * document base is the location of the ".ser" file if it was deserialized or the location of its class + * file if it was instantiated.</p> + * + * <p>A <code>ClassNotFoundException</code> is not only thrown when a class name was unknown + * but even when the class has public no-argument constructor + * (<code>IllegalAccessException</code> is wrapped) or an exception is thrown while + * invoking such a constructor (causing exception is wrapped).</p> + * + * @param cl ClassLoader to be used or <code>null</code> for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @param beanContext Context to which the newly created Bean should be added. + * @param initializer The AppletInitializer which is used for initializing <code>Applet</code> beans. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate( + ClassLoader cl, + String beanName, + BeanContext beanContext, + AppletInitializer initializer) + throws IOException, ClassNotFoundException + { + Object bean = null; + URL beanLocation = null; + URL classLocation = null; + + // Converts bean name into a resource name (eg. "a.b.c" -> "a/b/c"). + String resourceName = beanName.replace('.', '/'); + + /* Tries to get an input stream of the Bean, reading it as a system resource + * if no ClassLoader is present or as an application resource if a classloader + * is given. + */ + beanLocation = + (cl == null) + ? ClassLoader.getSystemResource(resourceName + ".ser") + : cl.getResource(resourceName + ".ser"); + + // Reads the serialized Bean from the returned URL. + if (beanLocation != null) + { + // Deserializes the bean instance. + ObjectInputStream ois = + (cl == null) + ? new ObjectInputStream(beanLocation.openStream()) + : new ClassLoaderObjectInputStream( + beanLocation.openStream(), + cl); + + bean = ois.readObject(); + + /* Implementation note: The result of ObjectInputStream.readObject() + * may have been null at this point (its a valid value to deserialize) + * and we explicitly want to try instantiation in such a case + * (this is important for compatibility). + */ + } + + // Instantiates the Bean using reflective instantiation if it has not been created yet. + if (bean == null) + { + // Makes sure that the deserialization was NOT done. + beanLocation = null; + + Class beanClass; + if (cl == null) + { + beanClass = Class.forName(beanName); + classLocation = + ClassLoader.getSystemResource(resourceName + ".class"); + } + else + { + beanClass = cl.loadClass(beanName); + classLocation = cl.getResource(resourceName + ".class"); + } + + // Instantiates and optionally registers the new bean. + try + { + bean = beanClass.newInstance(); + } + catch(Exception e) { + /* Wraps all kinds of Exceptions in a ClassNotFoundException (this behavior + * matches with official >= 1.5, this was different for <=1.4) + */ + throw new ClassNotFoundException(null, e); + } + } + + /* Applet beans are treated in the following way: + * - all AppletS get a default AppletStub + * - all AppletS are initialized using the AppletInitializer instance (if it is available) + * - as every other Bean Applets are added to a BeanContext if one is available + * - each instantiated Applet is initialized using Applet.init() (this is not done for deserialized ones) + * - finally AppletS get activated using the AppletInitializerS activate-Method + * + * The order of operations is important for compatibility. + */ + Applet applet = null; + if (bean instanceof Applet) + { + // Makes a second instanceof call unneccessary (instanceof is expensive). + applet = (Applet) bean; + + /* The AppletStub's code and document base is set as follows: + * The code base is always the URL from where the class data originated + * (without the package name). + * If the Applet was deserialized the document base is the location of + * the serialized instance (usually the ".ser" file) otherwise its the URL + * from where the class data originated (usually the absolute directory + * location of the ".class" file). + */ + applet.setStub( + new DummyAppletStub( + applet + .getClass() + .getProtectionDomain() + .getCodeSource() + .getLocation(), + (beanLocation == null) ? classLocation : beanLocation)); + + // Runs the Applet's initialization using an AppletInitializer. + if (initializer != null) + { + initializer.initialize(applet, beanContext); + } + } + + // Adds the new bean to its BeanContext. + if (beanContext != null) + { + beanContext.add(bean); + } + + if (applet != null) + { + + // Initializes an instantiated (not deserialized) Applet using its own method. + if (beanLocation == null) + { + applet.init(); + } + + // Runs the Applet's activation using an AppletInitializer. + if (initializer != null) + { + initializer.activate(applet); + } + } + + return bean; + } + + /** + * Returns the Bean as a different class type. + * This should be used instead of casting to get a new + * type view of a Bean, because in the future there may + * be new types of Bean, even Beans spanning multiple + * Objects. + * + * @param bean the Bean to cast. + * @param newClass the Class to cast it to. + * + * @return the Bean as a new view, or if the operation + * could not be performed, the Bean itself. + */ + public static Object getInstanceOf(Object bean, Class<?> newClass) + { + return bean; + } + + /** + * Determines whether the Bean can be cast to a different + * class type. + * This should be used instead of instanceof to determine + * a Bean's castability, because in the future there may + * be new types of Bean, even Beans spanning multiple + * Objects. + * + * @param bean the Bean to cast. + * @param newBeanClass the Class to cast it to. + * + * @return whether the Bean can be cast to the class type + * in question. + */ + public static boolean isInstanceOf(Object bean, Class<?> newBeanClass) + { + return newBeanClass.isInstance(bean); + } + + /** + * Returns whether the GUI is available to use. + * <p>Defaults to true.</p> + * + * @return whether the GUI is available to use. + */ + public static boolean isGuiAvailable() + { + return guiAvailable; + } + + /** + * Returns whether it is design time. Design time means + * we are in a RAD tool. + * <p>Defaults to false.</p> + * + * @return whether it is design time. + */ + public static boolean isDesignTime() + { + return designTime; + } + + /** + * Sets whether the GUI is available to use. + * + * @param guiAvailable whether the GUI is available to use. + */ + public static void setGuiAvailable(boolean guiAvailable) + throws SecurityException + { + Beans.guiAvailable = guiAvailable; + } + + /** + * Sets whether it is design time. Design time means we + * are in a RAD tool. + * + * @param designTime whether it is design time. + */ + public static void setDesignTime(boolean designTime) + throws SecurityException + { + Beans.designTime = designTime; + } + +} diff --git a/libjava/classpath/java/beans/ConstructorProperties.java b/libjava/classpath/java/beans/ConstructorProperties.java new file mode 100644 index 000000000..4c82c0033 --- /dev/null +++ b/libjava/classpath/java/beans/ConstructorProperties.java @@ -0,0 +1,72 @@ +/* ConstructorProperties.java - Associate constructor params with props + Copyright (C) 2006 Free Software Foundation + +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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.CONSTRUCTOR; + +/** + * An annotation used to associate the parameters of a + * constructor with the accessor methods that later provide + * access to these values. For example, the parameters of + * the constructor <code>Person(String name, int age)</code> + * may be linked to the bean's two accessors, <code>getName()</code> + * and <code>getAge()</code> using + * <code>@ConstructorProperties({"name","age"})</code>. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +@Documented @Retention(RUNTIME) @Target(CONSTRUCTOR) +public @interface ConstructorProperties +{ + + /** + * Contains the name of the accessor methods associated + * with each constructor parameter. + * + * @return the accessor method names corresponding to the + * constructor parameters. + */ + String[] value(); + +} diff --git a/libjava/classpath/java/beans/Customizer.java b/libjava/classpath/java/beans/Customizer.java new file mode 100644 index 000000000..bb6b4e2dd --- /dev/null +++ b/libjava/classpath/java/beans/Customizer.java @@ -0,0 +1,86 @@ +/* java.beans.Customizer + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** You may explicitly provide a Customizer for your Bean + ** class, which allows you complete control of the editing + ** of the Bean.<P> + ** + ** A Customizer is meant to be embedded in an RAD tool, + ** and thus must be a descendant of <CODE>java.awt.Component</CODE>.<P> + ** + ** It must also have a constructor with no arguments. This + ** is the constructor that will be called by the RAD tool to + ** instantiate the Customizer.<P> + ** + ** Over its lifetime, an instance of a Customizer will only + ** customize one single Bean. A new instance of the + ** Customizer will be instantiated to edit any other Beans.<P> + ** + ** The Customizer is responsible for notifying its + ** PropertyChangeListeners of any changes that are made, + ** according to the rules of PropertyChangeListeners (i.e. + ** notify the clients <EM>after</EM> the property has + ** changed). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 29 Jul 1998 + ** @see java.beans.BeanDescriptor.getCustomizerClass() + **/ + +public interface Customizer { + /** Set the object to Customize. This will always be a + ** Bean that had a BeanDescriptor indicating this + ** Customizer. + ** @param bean the Bean to customize. + **/ + void setObject(Object bean); + + /** Add a PropertyChangeListener. + ** @param l the PropertyChangeListener to add. + **/ + void addPropertyChangeListener(PropertyChangeListener l); + + /** Remove a PropertyChangeListener. + ** @param l the PropertyChangeListener to remove. + **/ + void removePropertyChangeListener(PropertyChangeListener l); +} diff --git a/libjava/classpath/java/beans/DefaultPersistenceDelegate.java b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java new file mode 100644 index 000000000..3a4d86cca --- /dev/null +++ b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java @@ -0,0 +1,211 @@ +/* DefaultPersistenceDelegate.java + Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** <p><code>DefaultPersistenceDelegate</code> is a {@link PersistenceDelegate} + * implementation that can be used to serialize objects which adhere to the + * Java Beans naming convention.</p> + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class DefaultPersistenceDelegate extends PersistenceDelegate +{ + + private String[] constructorPropertyNames; + + /** Using this constructor the object to be serialized will be instantiated + * with the default non-argument constructor. + */ + public DefaultPersistenceDelegate() + { + } + + /** This constructor allows to specify which Bean properties appear + * in the constructor. + * + * <p>The implementation reads the mentioned properties from the Bean + * instance and applies it in the given order to a corresponding + * constructor.</p> + * + * @param constructorPropertyNames The properties the Bean's constructor + * should be given to. + */ + public DefaultPersistenceDelegate(String[] constructorPropertyNames) + { + this.constructorPropertyNames = constructorPropertyNames; + } + + protected boolean mutatesTo(Object oldInstance, Object newInstance) + { + try + { + + return (constructorPropertyNames != null + && constructorPropertyNames.length > 0 + && oldInstance.getClass() + .getDeclaredMethod("equals", + new Class[] { Object.class }) != null) + ? oldInstance.equals(newInstance) + : super.mutatesTo(oldInstance, newInstance); + } + catch (NoSuchMethodException nsme) + { + return super.mutatesTo(oldInstance, newInstance); + } + } + + protected Expression instantiate(Object oldInstance, Encoder out) + { + Object[] args = null; + + try + { + // If there are property names in the array, then we create + // a corresponding argument array and store every + // argument in it. To retrieve an argument object we have + // dig up the right property in the bean class' BeanInfo + // object. + // This is so costly in terms of execution time I better + // not think twice about it ... + if (constructorPropertyNames != null) + { + args = new Object[constructorPropertyNames.length]; + + // Look up the properties of oldInstance's class to find matches for + // the + // names given in the constructor. + PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo( + oldInstance.getClass()).getPropertyDescriptors(); + + for (int i = 0; i < constructorPropertyNames.length; i++) + { + // Scan the property descriptions for a matching name. + for (int j = 0; j < propertyDescs.length; j++) + { + if (propertyDescs[i].getName().equals( + constructorPropertyNames[i])) + { + Method readMethod = propertyDescs[i].getReadMethod(); + + args[i] = readMethod.invoke(oldInstance); + } + } + } + } + + } + catch (IllegalAccessException iae) + { + out.getExceptionListener().exceptionThrown(iae); + } + catch (IllegalArgumentException iarge) + { + out.getExceptionListener().exceptionThrown(iarge); + } + catch (InvocationTargetException ite) + { + out.getExceptionListener().exceptionThrown(ite); + } + catch (IntrospectionException ie) + { + out.getExceptionListener().exceptionThrown(ie); + } + + return new Expression(oldInstance, oldInstance.getClass(), "new", args); + } + + protected void initialize(Class<?> type, Object oldInstance, + Object newInstance, Encoder out) + { + // Calling the supertype's implementation of initialize makes it + // possible that descendants of classes like AbstractHashMap + // or Hashtable are serialized correctly. This mechanism grounds on + // two other facts: + // * Each class which has not registered a special purpose + // PersistenceDelegate is handled by a DefaultPersistenceDelegate + // instance. + // * PersistenceDelegate.initialize() is implemented in a way that it + // calls the initialize method of the superclass' persistence delegate. + super.initialize(type, oldInstance, newInstance, out); + + // Suppresses the writing of property setting statements when this delegate + // is not used for the exact instance type. By doing so the following code + // is called only once per object. + if (type != oldInstance.getClass()) + return; + + try + { + PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo( + oldInstance.getClass()).getPropertyDescriptors(); + + for (int i = 0; i < propertyDescs.length; i++) + { + Method readMethod = propertyDescs[i].getReadMethod(); + Method writeMethod = propertyDescs[i].getWriteMethod(); + + if (readMethod != null && writeMethod != null) + { + Object oldValue = readMethod.invoke(oldInstance); + + if (oldValue != null) + out.writeStatement(new Statement(oldInstance, + writeMethod.getName(), + new Object[] { oldValue })); + } + } + } + catch (IntrospectionException ie) + { + out.getExceptionListener().exceptionThrown(ie); + } + catch (IllegalAccessException iae) + { + out.getExceptionListener().exceptionThrown(iae); + } + catch (InvocationTargetException ite) + { + out.getExceptionListener().exceptionThrown(ite); + } + } +} diff --git a/libjava/classpath/java/beans/DesignMode.java b/libjava/classpath/java/beans/DesignMode.java new file mode 100644 index 000000000..bc79361c1 --- /dev/null +++ b/libjava/classpath/java/beans/DesignMode.java @@ -0,0 +1,95 @@ +/* java.beans.DesignMode + Copyright (C) 1999, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * <code>BeanContextChild</code> implementors implement this to get information + * about whether they are in a design time or runtime environment. + * The reason this is restricted to <code>BeanContextChild</code>ren is that + * only things in the <code>BeanContext</code> hierarchy are given this + * information in the first place. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextChild + */ +public interface DesignMode +{ + + /** + * Use this name when firing <code>PropertyChangeEvent</code>s from your Bean. + */ + String PROPERTYNAME = "designTime"; + + /** + * The environment will call this method on your + * <code>BeanContextChild</code> when it is registered in a parent + * <code>BeanContext</code> or when behavior needs to switch from + * design time to runtime behavior (or vice versa). + * <P> + * + * <code>BeanContext</code>s are required to fire + * <code>PropertyChangeEvent</code>s when properties change. + * <code>designTime</code> is a property, and therefore when you + * implement <code>setDesignTime()</code>, you need to fire a + * <code>PropertyChangeEvent</code> with the old value, the new + * value and using <code>PROPERTYNAME</code> as the property name. + * + * @param designTime the new value of design time, + * <code>true</code> if it is design time, + * <code>false</code> if it is runtime. + * + * @fixme I'm frankly not really sure whether it's the case that + * the BeanContext can <em>change</em> the status of the Bean from + * design time to runtime. But it appears that it may be so. + * + * @see java.beans.PropertyChangeEvent + * @see java.beans.beancontext.BeanContext + * @see #PROPERTYNAME + */ + void setDesignTime(boolean designTime); + + /** + * This method should tell whether it is design time or runtime. + * @return <code>true</code> if design time, <code>false</code> if + * runtime. + */ + boolean isDesignTime(); + +} diff --git a/libjava/classpath/java/beans/Encoder.java b/libjava/classpath/java/beans/Encoder.java new file mode 100644 index 000000000..b3d232a31 --- /dev/null +++ b/libjava/classpath/java/beans/Encoder.java @@ -0,0 +1,433 @@ +/* Encoder.java + Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.DefaultExceptionListener; +import gnu.java.beans.encoder.ArrayPersistenceDelegate; +import gnu.java.beans.encoder.ClassPersistenceDelegate; +import gnu.java.beans.encoder.CollectionPersistenceDelegate; +import gnu.java.beans.encoder.MapPersistenceDelegate; +import gnu.java.beans.encoder.PrimitivePersistenceDelegate; + +import java.util.AbstractCollection; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class Encoder +{ + + /** + * An internal DefaultPersistenceDelegate instance that is used for every + * class that does not a have a special special PersistenceDelegate. + */ + private static PersistenceDelegate defaultPersistenceDelegate; + + private static PersistenceDelegate fakePersistenceDelegate; + + /** + * Stores the relation Class->PersistenceDelegate. + */ + private static HashMap delegates = new HashMap(); + + /** + * Stores the relation oldInstance->newInstance + */ + private IdentityHashMap candidates = new IdentityHashMap(); + + private ExceptionListener exceptionListener; + + /** + * A simple number that is used to restrict the access to writeExpression and + * writeStatement. The rule is that both methods should only be used when an + * object is written to the stream (= writeObject). Therefore accessCounter is + * incremented just before the call to writeObject and decremented afterwards. + * Then writeStatement and writeExpression allow execution only if + * accessCounter is bigger than zero. + */ + private int accessCounter = 0; + + public Encoder() + { + setupDefaultPersistenceDelegates(); + + setExceptionListener(null); + } + + /** + * Sets up a bunch of {@link PersistenceDelegate} instances which are needed + * for the basic working of a {@link Encoder}s. + */ + private static void setupDefaultPersistenceDelegates() + { + synchronized (delegates) + { + if (defaultPersistenceDelegate != null) + return; + + delegates.put(Class.class, new ClassPersistenceDelegate()); + + PersistenceDelegate pd = new PrimitivePersistenceDelegate(); + delegates.put(Boolean.class, pd); + delegates.put(Byte.class, pd); + delegates.put(Short.class, pd); + delegates.put(Integer.class, pd); + delegates.put(Long.class, pd); + delegates.put(Float.class, pd); + delegates.put(Double.class, pd); + + delegates.put(Object[].class, new ArrayPersistenceDelegate()); + + pd = new CollectionPersistenceDelegate(); + delegates.put(AbstractCollection.class, pd); + + pd = new MapPersistenceDelegate(); + delegates.put(java.util.AbstractMap.class, pd); + delegates.put(java.util.Hashtable.class, pd); + + defaultPersistenceDelegate = new DefaultPersistenceDelegate(); + delegates.put(Object.class, defaultPersistenceDelegate); + + // Creates a PersistenceDelegate implementation which is + // returned for 'null'. In practice this instance is + // not used in any way and is just here to be compatible + // with the reference implementation which returns a + // similar instance when calling getPersistenceDelegate(null) . + fakePersistenceDelegate = new PersistenceDelegate() + { + protected Expression instantiate(Object o, Encoder e) + { + return null; + } + }; + + } + } + + protected void writeObject(Object o) + { + // 'null' has no PersistenceDelegate and will not + // create an Expression which has to be cloned. + // However subclasses should be aware that writeObject + // may be called with a 'null' argument and should + // write the proper representation of it. + if (o == null) + return; + + PersistenceDelegate pd = getPersistenceDelegate(o.getClass()); + + accessCounter++; + pd.writeObject(o, this); + accessCounter--; + + } + + /** + * Sets the {@link ExceptionListener} instance to be used for reporting + * recorable exceptions in the instantiation and initialization sequence. If + * the argument is <code>null</code> a default instance will be used that + * prints the thrown exception to <code>System.err</code>. + */ + public void setExceptionListener(ExceptionListener listener) + { + exceptionListener = (listener != null) + ? listener : DefaultExceptionListener.INSTANCE; + } + + /** + * Returns the currently active {@link ExceptionListener} instance. + */ + public ExceptionListener getExceptionListener() + { + return exceptionListener; + } + + public PersistenceDelegate getPersistenceDelegate(Class<?> type) + { + // This is not specified but the JDK behaves like this. + if (type == null) + return fakePersistenceDelegate; + + // Treats all array classes in the same way and assigns + // them a shared PersistenceDelegate implementation tailored + // for array instantation and initialization. + if (type.isArray()) + return (PersistenceDelegate) delegates.get(Object[].class); + + PersistenceDelegate pd = (PersistenceDelegate) delegates.get(type); + + return (pd != null) ? pd : defaultPersistenceDelegate; + } + + /** + * Sets the {@link PersistenceDelegate} instance for the given class. + * <p> + * Note: Throws a <code>NullPointerException</code> if the argument is + * <code>null</code>. + * </p> + * <p> + * Note: Silently ignores PersistenceDelegates for Array types and primitive + * wrapper classes. + * </p> + * <p> + * Note: Although this method is not declared <code>static</code> changes to + * the {@link PersistenceDelegate}s affect <strong>all</strong> + * {@link Encoder} instances. <strong>In this implementation</strong> the + * access is thread safe. + * </p> + */ + public void setPersistenceDelegate(Class<?> type, + PersistenceDelegate delegate) + { + // If the argument is null this will cause a NullPointerException + // which is expected behavior. + + // This makes custom PDs for array, primitive types and their wrappers + // impossible but this is how the JDK behaves. + if (type.isArray() || type.isPrimitive() || type == Boolean.class + || type == Byte.class || type == Short.class || type == Integer.class + || type == Long.class || type == Float.class || type == Double.class) + return; + + synchronized (delegates) + { + delegates.put(type, delegate); + } + + } + + public Object remove(Object oldInstance) + { + return candidates.remove(oldInstance); + } + + /** + * Returns the replacement object which has been created by the encoder during + * the instantiation sequence or <code>null</code> if the object has not + * been processed yet. + * <p> + * Note: The <code>String</code> class acts as an endpoint for the + * inherently recursive algorithm of the {@link Encoder}. Therefore instances + * of <code>String</code> will always be returned by this method. In other + * words the assertion: <code> + * assert (anyEncoder.get(anyString) == anyString) + * </code< + * will always hold.</p> + * + * <p>Note: If <code>null</code> is requested, the result will + * always be <code>null</code>.</p> + */ + public Object get(Object oldInstance) + { + // String instances are handled in a special way. + // No one knows why this is not officially specified + // because this is a rather important design decision. + return (oldInstance == null) ? null : + (oldInstance.getClass() == String.class) ? + oldInstance : candidates.get(oldInstance); + } + + /** + * <p> + * Note: If you call this method not from within an object instantiation and + * initialization sequence it will be silently ignored. + * </p> + */ + public void writeStatement(Statement stmt) + { + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + Object target = stmt.getTarget(); + + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + } + + Object[] args = stmt.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + } + + Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs); + + try + { + newStmt.execute(); + } + catch (Exception e) + { + exceptionListener.exceptionThrown(e); + } + + } + + /** + * <p> + * Note: If you call this method not from within an object instantiation and + * initialization sequence it will be silently ignored. + * </p> + */ + public void writeExpression(Expression expr) + { + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + Object target = expr.getTarget(); + Object value = null; + Object newValue = null; + + try + { + value = expr.getValue(); + } + catch (Exception e) + { + exceptionListener.exceptionThrown(e); + return; + } + + + newValue = get(value); + + if (newValue == null) + { + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + + // May happen if exception was thrown. + if (newTarget == null) + { + return; + } + } + + Object[] args = expr.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + } + + Expression newExpr = new Expression(newTarget, expr.getMethodName(), + newArgs); + + // Fakes the result of Class.forName(<primitiveType>) to make it possible + // to hand such a type to the encoding process. + if (value instanceof Class && ((Class) value).isPrimitive()) + newExpr.setValue(value); + + // Instantiates the new object. + try + { + newValue = newExpr.getValue(); + + candidates.put(value, newValue); + } + catch (Exception e) + { + exceptionListener.exceptionThrown(e); + + return; + } + + writeObject(value); + + } + else if(value.getClass() == String.class || value.getClass() == Class.class) + { + writeObject(value); + } + + } + + /** Returns whether the given class is an immutable + * type which has to be handled differently when serializing it. + * + * <p>Immutable objects always have to be instantiated instead of + * modifying an existing instance.</p> + * + * @param type The class to test. + * @return Whether the first argument is an immutable type. + */ + boolean isImmutableType(Class type) + { + return type == String.class || type == Class.class + || type == Integer.class || type == Boolean.class + || type == Byte.class || type == Short.class + || type == Long.class || type == Float.class + || type == Double.class; + } + + /** Sets the stream candidate for a given object. + * + * @param oldObject The object given to the encoder. + * @param newObject The object the encoder generated. + */ + void putCandidate(Object oldObject, Object newObject) + { + candidates.put(oldObject, newObject); + } + +} diff --git a/libjava/classpath/java/beans/EventHandler.java b/libjava/classpath/java/beans/EventHandler.java new file mode 100644 index 000000000..967ba8294 --- /dev/null +++ b/libjava/classpath/java/beans/EventHandler.java @@ -0,0 +1,604 @@ +/* java.beans.EventHandler + Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * <p>EventHandler forms a bridge between dynamically created listeners and + * arbitrary properties and methods.</p> + * + * <p>You can use this class to easily create listener implementations for + * some basic interactions between an event source and its target. Using + * the three static methods named <code>create</code> you can create + * these listener implementations.</p> + * + * <p>See the documentation of each method for usage examples.</p> + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Robert Schuster (thebohemian@gmx.net) + * @since 1.4 + */ +public class EventHandler implements InvocationHandler +{ + // The name of the method that will be implemented. If null, any method. + private String listenerMethod; + + // The object to call action on. + private Object target; + + // The name of the method or property setter in target. + private String action; + + // The property to extract from an event passed to listenerMethod. + private String property; + + // The target objects Class. + private Class targetClass; + + // String class doesn't already have a capitalize routine. + private String capitalize(String s) + { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * Creates a new <code>EventHandler</code> instance. + * + * <p>Typical creation is done with the create method, not by knewing an + * EventHandler.</p> + * + * <p>This constructs an EventHandler that will connect the method + * listenerMethodName to target.action, extracting eventPropertyName from + * the first argument of listenerMethodName. and sending it to action.</p> + * + * <p>Throws a <code>NullPointerException</code> if the <code>target</code> + * argument is <code>null</code>. + * + * @param target Object that will perform the action. + * @param action A property or method of the target. + * @param eventPropertyName A readable property of the inbound event. + * @param listenerMethodName The listener method name triggering the action. + */ + public EventHandler(Object target, String action, String eventPropertyName, + String listenerMethodName) + { + this.target = target; + + // Retrieving the class is done for two reasons: + // 1) The class object is needed very frequently in the invoke() method. + // 2) The constructor should throw a NullPointerException if target is null. + targetClass = target.getClass(); + + this.action = action; // Turn this into a method or do we wait till + // runtime + property = eventPropertyName; + listenerMethod = listenerMethodName; + } + + /** + * Returns the event property name. + */ + public String getEventPropertyName() + { + return property; + } + + /** + * Returns the listener's method name. + */ + public String getListenerMethodName() + { + return listenerMethod; + } + + /** + * Returns the target object. + */ + public Object getTarget() + { + return target; + } + + /** + * Returns the action method name. + */ + public String getAction() + { + return action; + } + + // Fetch a qualified property like a.b.c from object o. The properties can + // be boolean isProp or object getProp properties. + // + // Returns a length 2 array with the first entry containing the value + // extracted from the property, and the second entry contains the class of + // the method return type. + // + // We play this game because if the method returns a native type, the return + // value will be a wrapper. If we then take the type of the wrapper and use + // it to locate the action method that takes the native type, it won't match. + private Object[] getProperty(Object o, String prop) + { + // Isolate the first property name from a.b.c. + int pos; + String rest = null; + if ((pos = prop.indexOf('.')) != -1) + { + rest = prop.substring(pos + 1); + prop = prop.substring(0, pos); + } + + // Find a method named getProp. It could be isProp instead. + Method getter; + try + { + // Look for boolean property getter isProperty + getter = o.getClass().getMethod("is" + capitalize(prop)); + } + catch (NoSuchMethodException nsme1) + { + try { + // Look for regular property getter getProperty + getter = o.getClass().getMethod("get" + capitalize(prop)); + } catch(NoSuchMethodException nsme2) { + try { + // Finally look for a method of the name prop + getter = o.getClass().getMethod(prop); + } catch(NoSuchMethodException nsme3) { + // Ok, give up with an intelligent hint for the user. + throw new RuntimeException("Method not called: Could not find a property or method '" + prop + + "' in " + o.getClass() + " while following the property argument '" + property + "'."); + } + } + } + try { + Object val = getter.invoke(o); + + if (rest != null) + return getProperty(val, rest); + + return new Object[] {val, getter.getReturnType()}; + } catch(InvocationTargetException ite) { + throw new RuntimeException("Method not called: Property or method '" + prop + "' has thrown an exception.", ite); + } catch(IllegalAccessException iae) { + // This cannot happen because we looked up method with Class.getMethod() + // which returns public methods only. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + * Invokes the <code>EventHandler</code>. + * + * <p>This method is normally called by the listener's proxy implementation.</p> + * + * @param proxy The listener interface that is implemented using + * the proxy mechanism. + * @param method The method that was called on the proxy instance. + * @param arguments The arguments which where given to the method. + * @throws Throwable <code>NoSuchMethodException</code> is thrown when the EventHandler's + * action method or property cannot be found. + */ + public Object invoke(Object proxy, Method method, Object[] arguments) + { + try { + // The method instance of the target object. We have to find out which + // one we have to invoke. + Method actionMethod = null; + + // Listener methods that weren't specified are ignored. If listenerMethod + // is null, then all listener methods are processed. + if (listenerMethod != null && !method.getName().equals(listenerMethod)) + return null; + + // If a property is defined we definitely need a valid object at + // arguments[0] that can be used to retrieve a value to which the + // property of the target gets set. + if(property != null) { + // Extracts the argument. We will let it fail with a NullPointerException + // the caller used a listener method that has no arguments. + Object event = arguments[0]; + + // Obtains the property XXX propertyType keeps showing up null - why? + // because the object inside getProperty changes, but the ref variable + // can't change this way, dolt! need a better way to get both values out + // - need method and object to do the invoke and get return type + Object v[] = getProperty(event, property); + Object[] args = new Object[] { v[0] }; + + // Changes the class array that controls which method signature we are going + // to look up in the target object. + Class[] argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + // Look for a property setter for action. + actionMethod = targetClass.getMethod("set" + capitalize(action), argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + // If action as property didn't work, try as method later. + } + + argTypes[0] = nextClass(argTypes[0]); + } + + // We could not find a suitable setter method. Now we try again interpreting + // action as the method name itself. + // Since we probably have changed the block local argTypes array + // we need to rebuild it. + argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + actionMethod = targetClass.getMethod(action, argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + } + + argTypes[0] = nextClass(argTypes[0]); + } + + throw new RuntimeException("Method not called: Could not find a public method named '" + + action + "' in target " + targetClass + " which takes a '" + + v[1] + "' argument or a property of this type."); + } + + // If property was null we will search for a no-argument method here. + // Note: The ordering of method lookups is important because we want to prefer no-argument + // calls like the JDK does. This means if we have actionMethod() and actionMethod(Event) we will + // call the first *EVEN* if we have a valid argument for the second method. This is behavior compliant + // to the JDK. + // If actionMethod() is not available but there is a actionMethod(Event) we take this. That makes us + // more specification compliant than the JDK itself because this one will fail in such a case. + try + { + actionMethod = targetClass.getMethod(action); + } + catch(NoSuchMethodException nsme) + { + // Note: If we want to be really strict the specification says that a no-argument method should + // accept an EventObject (or subclass I guess). However since the official implementation is broken + // anyways, it's more flexible without the EventObject restriction and we are compatible on everything + // else this can stay this way. + if(arguments != null && arguments.length >= 1/* && arguments[0] instanceof EventObject*/) { + Class[] targetArgTypes = new Class[] { initClass(arguments[0].getClass()) }; + + while(targetArgTypes[0] != null) { + try + { + // If no property exists we expect the first element of the arguments to be + // an EventObject which is then applied to the target method. + + actionMethod = targetClass.getMethod(action, targetArgTypes); + + return actionMethod.invoke(target, new Object[] { arguments[0] }); + } + catch(NoSuchMethodException nsme2) + { + + } + + targetArgTypes[0] = nextClass(targetArgTypes[0]); + } + + } + } + + // If we do not have a Method instance at this point this means that all our tries + // failed. The JDK throws an ArrayIndexOutOfBoundsException in this case. + if(actionMethod == null) + throw new ArrayIndexOutOfBoundsException(0); + + // Invoke target.action(property) + return actionMethod.invoke(target); + } catch(InvocationTargetException ite) { + throw new RuntimeException(ite.getCause()); + } catch(IllegalAccessException iae) { + // Cannot happen because we always use getMethod() which returns public + // methods only. Otherwise there is something seriously broken in + // GNU Classpath. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + * <p>Returns the primitive type for every wrapper class or the + * class itself if it is no wrapper class.</p> + * + * <p>This is needed because to be able to find both kinds of methods: + * One that takes a wrapper class as the first argument and one that + * accepts a primitive instead.</p> + */ + private Class initClass(Class klass) { + if(klass == Boolean.class) { + return Boolean.TYPE; + } else if(klass == Byte.class) { + return Byte.TYPE; + } else if(klass == Short.class) { + return Short.TYPE; + } else if(klass == Integer.class) { + return Integer.TYPE; + } else if(klass == Long.class) { + return Long.TYPE; + } else if(klass == Float.class) { + return Float.TYPE; + } else if(klass == Double.class) { + return Double.TYPE; + } else { + return klass; + } + } + + /** + * + * + * @param klass + * @return + */ + private Class nextClass(Class klass) { + if(klass == Boolean.TYPE) { + return Boolean.class; + } else if(klass == Byte.TYPE) { + return Byte.class; + } else if(klass == Short.TYPE) { + return Short.class; + } else if(klass == Integer.TYPE) { + return Integer.class; + } else if(klass == Long.TYPE) { + return Long.class; + } else if(klass == Float.TYPE) { + return Float.class; + } else if(klass == Double.TYPE) { + return Double.class; + } else { + return klass.getSuperclass(); + } + } + + /** + * <p>Constructs an implementation of <code>listenerInterface</code> + * to dispatch events.</p> + * + * <p>You can use such an implementation to simply call a public + * no-argument method of an arbitrary target object or to forward + * the first argument of the listener method to the target method.</p> + * + * <p>Call this method like:</p> + * <code> + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, target, "dispose")); + * </code> + * + * <p>to achieve the following behavior:</p> + * <code> + * button.addActionListener(new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * target.dispose(); + * } + * }); + * </code> + * + * <p>That means if you need a listener implementation that simply calls a + * a no-argument method on a given instance for <strong>each</strong> + * method of the listener interface.</p> + * + * <p>Note: The <code>action</code> is interpreted as a method name. If your target object + * has no no-argument method of the given name the EventHandler tries to find + * a method with the same name but which can accept the first argument of the + * listener method. Usually this will be an event object but any other object + * will be forwarded, too. Keep in mind that using a property name instead of a + * real method here is wrong and will throw an <code>ArrayIndexOutOfBoundsException</code> + * whenever one of the listener methods is called.<p/> + * + * <p>The <code>EventHandler</code> will automatically convert primitives + * to their wrapper class and vice versa. Furthermore it will call + * a target method if it accepts a superclass of the type of the + * first argument of the listener method.</p> + * + * <p>In case that the method of the target object throws an exception + * it will be wrapped in a <code>RuntimeException</code> and thrown out + * of the listener method.</p> + * + * <p>In case that the method of the target object cannot be found an + * <code>ArrayIndexOutOfBoundsException</code> will be thrown when the + * listener method is invoked.</p> + * + * <p>A call to this method is equivalent to: + * <code>create(listenerInterface, target, action, null, null)</code></p> + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target property or method to invoke. + * @return A constructed proxy object. + */ + public static <T> T create(Class<T> listenerInterface, Object target, + String action) + { + return create(listenerInterface, target, action, null, null); + } + + /** + * <p>Constructs an implementation of <code>listenerInterface</code> + * to dispatch events.</p> + * + * <p>Use this method if you want to create an implementation that retrieves + * a property value from the <b>first</b> argument of the listener method + * and applies it to the target's property or method. This first argument + * of the listener is usually an event object but any other object is + * valid, too.</p> + * + * <p>You can set the value of <code>eventPropertyName</code> to "prop" + * to denote the retrieval of a property named "prop" from the event + * object. In case that no such property exists the <code>EventHandler</code> + * will try to find a method with that name.</p> + * + * <p>If you set <code>eventPropertyName</code> to a value like this "a.b.c" + * <code>EventHandler</code> will recursively evaluate the properties "a", "b" + * and "c". Again if no property can be found the <code>EventHandler</code> + * tries a method name instead. This allows mixing the names, too: "a.toString" + * will retrieve the property "a" from the event object and will then call + * the method "toString" on it.</p> + * + * <p>An exception thrown in any of these methods will provoke a + * <code>RuntimeException</code> to be thrown which contains an + * <code>InvocationTargetException</code> containing the triggering exception.</p> + * + * <p>If you set <code>eventPropertyName</code> to a non-null value the + * <code>action</code> parameter will be interpreted as a property name + * or a method name of the target object.</p> + * + * <p>Any object retrieved from the event object and applied to the + * target will converted from primitives to their wrapper class or + * vice versa or applied to a method that accepts a superclass + * of the object.</p> + * + * <p>Examples:</p> + * <p>The following code:</p><code> + * button.addActionListener( + * new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * Object o = ae.getSource().getClass().getName(); + * textField.setText((String) o); + * } + * }); + * </code> + * + * <p>Can be expressed using the <code>EventHandler</code> like this:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "text", "source.class.name"); + * <code> + * </p> + * + * <p>As said above you can specify the target as a method, too:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.class.name"); + * <code> + * </p> + * + * <p>Furthermore you can use method names in the property:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "getSource.getClass.getName"); + * <code> + * </p> + * + * <p>Finally you can mix names:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.getClass.name"); + * <code> + * </p> + * + * <p>A call to this method is equivalent to: + * <code>create(listenerInterface, target, action, null, null)</code> + * </p> + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target property or method to invoke. + * @param eventPropertyName Name of property to extract from event. + * @return A constructed proxy object. + */ + public static <T> T create(Class<T> listenerInterface, Object target, + String action, String eventPropertyName) + { + return create(listenerInterface, target, action, eventPropertyName, null); + } + + /** + * <p>Constructs an implementation of <code>listenerInterface</code> + * to dispatch events.</p> + * + * <p>Besides the functionality described for {@link create(Class, Object, String)} + * and {@link create(Class, Object, String, String)} this method allows you + * to filter the listener method that should have an effect. Look at these + * method's documentation for more information about the <code>EventHandler</code>'s + * usage.</p> + * + * <p>If you want to call <code>dispose</code> on a <code>JFrame</code> instance + * when the <code>WindowListener.windowClosing()</code> method was invoked use + * the following code:</p> + * <p> + * <code> + * EventHandler.create(WindowListener.class, jframeInstance, "dispose", null, "windowClosing"); + * </code> + * </p> + * + * <p>A <code>NullPointerException</code> is thrown if the <code>listenerInterface</code> + * or <code>target</code> argument are <code>null</code>. + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target method name to invoke. + * @param eventPropertyName Name of property to extract from event. + * @param listenerMethodName Listener method to implement. + * @return A constructed proxy object. + */ + public static <T> T create(Class<T> listenerInterface, Object target, + String action, String eventPropertyName, + String listenerMethodName) + { + // Create EventHandler instance + EventHandler eh = new EventHandler(target, action, eventPropertyName, + listenerMethodName); + + // Create proxy object passing in the event handler + Object proxy = Proxy.newProxyInstance(listenerInterface.getClassLoader(), + new Class<?>[] {listenerInterface}, + eh); + + return (T) proxy; + } +} diff --git a/libjava/classpath/java/beans/EventSetDescriptor.java b/libjava/classpath/java/beans/EventSetDescriptor.java new file mode 100644 index 000000000..3f537c743 --- /dev/null +++ b/libjava/classpath/java/beans/EventSetDescriptor.java @@ -0,0 +1,763 @@ +/* java.beans.EventSetDescriptor + Copyright (C) 1998, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import gnu.java.lang.ClassHelper; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Vector; + +/** + * EventSetDescriptor describes the hookup between an event source class and + * an event listener class. + * + * <p>EventSets have several attributes: the listener class, + * the events that can be fired to the listener (methods in the listener + * class), and an add and remove listener method from the event firer's + * class. + * </p> + * + * <p> + * The methods have these constraints on them: + * <ul> + * <li>event firing methods: must have <code>void</code> return value. Any + * parameters and exceptions are allowed. May be public, protected or + * package-protected. (Don't ask me why that is, I'm just following the spec. + * The only place it is even mentioned is in the Java Beans white paper, and + * there it is only implied.)</li> + * + * <li>add listener method: must have <code>void</code> return value. Must + * take exactly one argument, of the listener class's type. May fire either + * zero exceptions, or one exception of type + * <code>java.util.TooManyListenersException</code>. + * Must be public.</li> + * + * <li>remove listener method: must have <code>void</code> return value. Must + * take exactly one argument, of the listener class's type. May not fire any + * exceptions. Must be public.</li> + * </ul> + * + * <p> + * A final constraint is that event listener classes must extend from + * EventListener. + * </p> + * + * <p> + * There are also various design patterns associated with some of the methods + * of construction. Those are explained in more detail in the appropriate + * constructors. + * </p> + * + * <p> + * <strong>Documentation Convention:</strong> for proper Internalization of + * Beans inside an RAD tool, sometimes there are two names for a property or + * method: a programmatic, or locale-independent name, which can be used + * anywhere, and a localized, display name, for ease of use. In the + * documentation I will specify different String values as either + * <em>programmatic</em> or <em>localized</em> to make this distinction clear. + * + * @author John Keiser + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.1 + */ + +public class EventSetDescriptor extends FeatureDescriptor +{ + private Method addListenerMethod; + + private Method removeListenerMethod; + + private Class listenerType; + + private MethodDescriptor[] listenerMethodDescriptors; + + private Method[] listenerMethods; + + private Method getListenerMethod; + + private boolean unicast; + + private boolean inDefaultEventSet = true; + + /** + * Creates a new <code>EventSetDescriptor</code<. + * + * <p> + * This version of the constructor enforces the rules imposed on the methods + * described at the top of this class, as well as searching for: + * </p> + * + * <ol> + * <li> + * The event-firing method must be non-private with signature <code>void + * <listenerMethodName>(<eventSetName>Event)</code> (where + * <code><eventSetName></code> has its first character capitalized + * by the constructor and the Event is a descendant of + * {@link java.util.EventObject}) in class <code>listenerType</code> + * (any exceptions may be thrown). <b>Implementation note:</b> Note that + * there could conceivably be multiple methods with this type of signature + * (example: <code>java.util.MouseEvent</code> vs. + * <code>my.very.own.MouseEvent</code>). In this implementation, all + * methods fitting the description will be put into the + * <code>EventSetDescriptor</code>, even though the spec says only one + * should be chosen (they probably weren't thinking as pathologically as I + * was). I don't like arbitrarily choosing things. If your class has only one + * such signature, as most do, you'll have no problems.</li> + * + * <li>The add and remove methods must be public and named <code>void + * add<eventSetName>Listener(<listenerType>)</code> and + * <code>void remove<eventSetName>Listener(<listenerType>)</code> + * in in class <code>eventSourceClass</code>, where + * <code><eventSetName></code> will have its first letter capitalized. + * Standard exception rules (see class description) apply.</li> + * </ol> + * + * @param eventSourceClass + * the class containing the add/remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). This will + * be used to generate the name of the event object as well as the + * names of the add and remove methods. + * @param listenerType + * the class containing the event firing method. + * @param listenerMethodName + * the name of the event firing method. + * @exception IntrospectionException + * if listenerType is not an EventListener, or if methods are not + * found or are invalid. + */ + public EventSetDescriptor(Class<?> eventSourceClass, String eventSetName, + Class<?> listenerType, String listenerMethodName) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + String[] names = new String[1]; + names[0] = listenerMethodName; + + try + { + eventSetName = Character.toUpperCase(eventSetName.charAt(0)) + + eventSetName.substring(1); + } + catch (StringIndexOutOfBoundsException e) + { + eventSetName = ""; + } + + findMethods(eventSourceClass, listenerType, names, + "add" + eventSetName + "Listener", + "remove" + eventSetName + "Listener", eventSetName + "Event"); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** + * Creates a new <code>EventSetDescriptor</code>. + * + * <p>This form of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + * </p> + * + * @param eventSourceClass + * the class containing the add and remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the event firing methods. + * @param listenerMethodNames + * the names of the even firing methods. + * @param addListenerMethodName + * the name of the add listener method. + * @param removeListenerMethodName + * the name of the remove listener method. + * @exception IntrospectionException + * if listenerType is not an EventListener or if methods are not + * found or are invalid. + */ + public EventSetDescriptor(Class<?> eventSourceClass, String eventSetName, + Class<?> listenerType, String[] listenerMethodNames, + String addListenerMethodName, + String removeListenerMethodName) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + findMethods(eventSourceClass, listenerType, listenerMethodNames, + addListenerMethodName, removeListenerMethodName, null); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** + * Creates a new <code>EventSetDescriptor</code>. + * + * <p> + * This variant of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + * </p> + * <p> + * A valid GetListener method is public, flags no exceptions and has one + * argument which is of type <code>Class</code> + * {@link java.awt.Component#getListeners(Class)} is such a method. + * </p> + * <p> + * Note: The validity of the return value of the GetListener method is not + * checked. + * </p> + * + * @param eventSourceClass + * the class containing the add and remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the event firing methods. + * @param listenerMethodNames + * the names of the even firing methods. + * @param addListenerMethodName + * the name of the add listener method. + * @param removeListenerMethodName + * the name of the remove listener method. + * @param getListenerMethodName + * Name of a method which returns the array of listeners. + * @exception IntrospectionException + * if listenerType is not an EventListener or if methods are not + * found or are invalid. + * @since 1.4 + */ + public EventSetDescriptor(Class<?> eventSourceClass, String eventSetName, + Class<?> listenerType, String[] listenerMethodNames, + String addListenerMethodName, + String removeListenerMethodName, + String getListenerMethodName) + throws IntrospectionException + { + this(eventSourceClass, eventSetName, listenerType, listenerMethodNames, + addListenerMethodName, removeListenerMethodName); + + Method newGetListenerMethod = null; + + try + { + newGetListenerMethod + = eventSourceClass.getMethod(getListenerMethodName, + new Class[] { Class.class }); + } + catch (NoSuchMethodException nsme) + { + throw (IntrospectionException) + new IntrospectionException("No method named " + getListenerMethodName + + " in class " + listenerType + + " which can be used as" + + " getListenerMethod.").initCause(nsme); + } + + // Note: This does not check the return value (which + // should be EventListener[]) but the JDK does not either. + + getListenerMethod = newGetListenerMethod; + + } + + /** + * Creates a new <code>EventSetDescriptor.</code> + * + * <p> + * This variant of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + * </p> + * <p> + * A valid GetListener method is public, flags no exceptions and has one + * argument which is of type <code>Class</code> + * {@link java.awt.Component#getListeners(Class)} is such a method. + * </p> + * <p> + * Note: The validity of the return value of the GetListener method is not + * checked. + * </p> + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethods + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @param getListenerMethod + * The method which returns an array of the listeners. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + * @since 1.4 + */ + public EventSetDescriptor(String eventSetName, Class<?> listenerType, + Method[] listenerMethods, Method addListenerMethod, + Method removeListenerMethod, + Method getListenerMethod) + throws IntrospectionException + { + this(eventSetName, listenerType, listenerMethods, addListenerMethod, + removeListenerMethod); + + // Do no checks if the getListenerMethod is null. + if (getListenerMethod.getParameterTypes().length != 1 + || getListenerMethod.getParameterTypes()[0] != Class.class + || getListenerMethod.getExceptionTypes().length > 0 + || !Modifier.isPublic(getListenerMethod.getModifiers())) + throw new IntrospectionException("GetListener method is invalid."); + + // Note: This does not check the return value (which + // should be EventListener[]) but the JDK does not either. + + this.getListenerMethod = getListenerMethod; + } + + /** + * Creates a new <code>EventSetDescriptor</code>. + * + * <p>This form of constructor allows you to explicitly say which methods + * do what, and no reflection is done by the <code>EventSetDescriptor</code>. + * The methods are, however, checked to ensure that they follow the rules + * set forth at the top of the class. + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethods + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + */ + public EventSetDescriptor(String eventSetName, Class<?> listenerType, + Method[] listenerMethods, Method addListenerMethod, + Method removeListenerMethod) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + this.listenerMethods = listenerMethods; + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** Creates a new <code>EventSetDescriptor</code>. + * + * <p>This form of constructor allows you to explicitly say which methods do + * what, and no reflection is done by the <code>EventSetDescriptor</code>. + * The methods are, however, checked to ensure that they follow the rules + * set forth at the top of the class. + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethodDescriptors + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + */ + public EventSetDescriptor(String eventSetName, Class<?> listenerType, + MethodDescriptor[] listenerMethodDescriptors, + Method addListenerMethod, + Method removeListenerMethod) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + this.listenerMethodDescriptors = listenerMethodDescriptors; + this.listenerMethods = new Method[listenerMethodDescriptors.length]; + for (int i = 0; i < this.listenerMethodDescriptors.length; i++) + { + this.listenerMethods[i] + = this.listenerMethodDescriptors[i].getMethod(); + } + + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** Returns the class that contains the event firing methods. + */ + public Class<?> getListenerType() + { + return listenerType; + } + + /** Returns the event firing methods. + */ + public Method[] getListenerMethods() + { + return listenerMethods; + } + + /** Returns the event firing methods as {@link MethodDescriptor}. + */ + public MethodDescriptor[] getListenerMethodDescriptors() + { + if (listenerMethodDescriptors == null) + { + listenerMethodDescriptors + = new MethodDescriptor[listenerMethods.length]; + + for (int i = 0; i < listenerMethods.length; i++) + { + listenerMethodDescriptors[i] + = new MethodDescriptor(listenerMethods[i]); + } + } + + return listenerMethodDescriptors; + } + + /** Returns the add listener method. + */ + public Method getAddListenerMethod() + { + return addListenerMethod; + } + + /* Returns the remove listener method. + */ + public Method getRemoveListenerMethod() + { + return removeListenerMethod; + } + + /** + * Returns the method that retrieves the listeners or <code>null</code> if + * it does not exist. + */ + public Method getGetListenerMethod() + { + return getListenerMethod; + } + + /** Sets whether or not multiple listeners may be added. + * + * @param unicast + * whether or not multiple listeners may be added. + */ + public void setUnicast(boolean unicast) + { + this.unicast = unicast; + } + + /** Returns whether or not multiple listeners may be added. + * (Defaults to false.) + */ + public boolean isUnicast() + { + return unicast; + } + + /** Sets whether or not this is in the default event set. + * + * @param inDefaultEventSet + * whether this is in the default event set. + */ + public void setInDefaultEventSet(boolean inDefaultEventSet) + { + this.inDefaultEventSet = inDefaultEventSet; + } + + /** Returns whether or not this is in the default event set. + * (Defaults to true.) + */ + public boolean isInDefaultEventSet() + { + return inDefaultEventSet; + } + + private void checkAddListenerUnicast() throws IntrospectionException + { + Class[] addListenerExceptions = this.addListenerMethod.getExceptionTypes(); + if (addListenerExceptions.length > 1) + { + throw new IntrospectionException( + "Listener add method throws too many exceptions."); + } + else if (addListenerExceptions.length == 1 + && !java.util.TooManyListenersException.class + .isAssignableFrom(addListenerExceptions[0])) + { + throw new IntrospectionException( + "Listener add method throws too many exceptions."); + } + } + + private void checkMethods() throws IntrospectionException + { + if (!addListenerMethod.getDeclaringClass() + .isAssignableFrom(removeListenerMethod.getDeclaringClass()) + && !removeListenerMethod.getDeclaringClass() + .isAssignableFrom(addListenerMethod.getDeclaringClass())) + { + throw new IntrospectionException( + "add and remove listener methods do not come from the" + + " same class. This is bad."); + } + if (!addListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || addListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(addListenerMethod.getParameterTypes()[0]) + || !Modifier.isPublic(addListenerMethod.getModifiers())) + { + throw new IntrospectionException("Add Listener Method invalid."); + } + if (!removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || removeListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(removeListenerMethod.getParameterTypes()[0]) + || removeListenerMethod.getExceptionTypes().length > 0 + || !Modifier.isPublic(removeListenerMethod.getModifiers())) + { + throw new IntrospectionException("Remove Listener Method invalid."); + } + + for (int i = 0; i < listenerMethods.length; i++) + { + if (!listenerMethods[i].getReturnType().equals(java.lang.Void.TYPE) + || Modifier.isPrivate(listenerMethods[i].getModifiers())) + { + throw new IntrospectionException("Event Method " + + listenerMethods[i].getName() + + " non-void or private."); + } + if (!listenerMethods[i].getDeclaringClass() + .isAssignableFrom(listenerType)) + { + throw new IntrospectionException("Event Method " + + listenerMethods[i].getName() + + " not from class " + + listenerType.getName()); + } + } + } + + private void findMethods(Class eventSourceClass, Class listenerType, + String listenerMethodNames[], + String addListenerMethodName, + String removeListenerMethodName, + String absurdEventClassCheckName) + throws IntrospectionException + { + + /* Find add listener method and remove listener method. */ + Class[] listenerArgList = new Class[1]; + listenerArgList[0] = listenerType; + try + { + this.addListenerMethod + = eventSourceClass.getMethod(addListenerMethodName, + listenerArgList); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "SecurityException trying to access method " + + addListenerMethodName + "."); + } + catch (NoSuchMethodException E) + { + throw new IntrospectionException("Could not find method " + + addListenerMethodName + "."); + } + + if (this.addListenerMethod == null + || !this.addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException( + "Add listener method does not exist, is not public," + + " or is not void."); + } + + try + { + this.removeListenerMethod + = eventSourceClass.getMethod(removeListenerMethodName, + listenerArgList); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "SecurityException trying to access method " + + removeListenerMethodName + "."); + } + catch (NoSuchMethodException E) + { + throw new IntrospectionException("Could not find method " + + removeListenerMethodName + "."); + } + if (this.removeListenerMethod == null + || !this.removeListenerMethod.getReturnType() + .equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException( + "Remove listener method does not exist, is not public," + + " or is not void."); + } + + /* Find the listener methods. */ + Method[] methods; + try + { + methods = ClassHelper.getAllMethods(listenerType); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "Security: You cannot access fields in this class."); + } + + Vector chosenMethods = new Vector(); + boolean[] listenerMethodFound = new boolean[listenerMethodNames.length]; + for (int i = 0; i < methods.length; i++) + { + if (Modifier.isPrivate(methods[i].getModifiers())) + { + continue; + } + Method currentMethod = methods[i]; + Class retval = currentMethod.getReturnType(); + if (retval.equals(java.lang.Void.TYPE)) + { + for (int j = 0; j < listenerMethodNames.length; j++) + { + if (currentMethod.getName().equals(listenerMethodNames[j]) + && (absurdEventClassCheckName == null + || (currentMethod.getParameterTypes().length == 1 + && ((currentMethod.getParameterTypes()[0]) + .getName().equals(absurdEventClassCheckName) + || (currentMethod.getParameterTypes()[0]) + .getName().endsWith("." + absurdEventClassCheckName))))) + { + chosenMethods.addElement(currentMethod); + listenerMethodFound[j] = true; + } + } + } + } + + /* Make sure we found all the methods we were looking for. */ + for (int i = 0; i < listenerMethodFound.length; i++) + { + if (!listenerMethodFound[i]) + { + throw new IntrospectionException("Could not find event method " + + listenerMethodNames[i]); + } + } + + /* Now that we've chosen the listener methods we want, store them. */ + this.listenerMethods = new Method[chosenMethods.size()]; + for (int i = 0; i < chosenMethods.size(); i++) + { + this.listenerMethods[i] = (Method) chosenMethods.elementAt(i); + } + } + +} diff --git a/libjava/classpath/java/beans/ExceptionListener.java b/libjava/classpath/java/beans/ExceptionListener.java new file mode 100644 index 000000000..0eeb8a956 --- /dev/null +++ b/libjava/classpath/java/beans/ExceptionListener.java @@ -0,0 +1,57 @@ +/* ExceptionListener.java -- listen for recoverable internal exceptions + Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * This interface allows a class to monitor internal exceptions, to try to + * recover from them. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public interface ExceptionListener +{ + /** + * Fired after an exception occurs. + * + * @param e the trapped exception + */ + void exceptionThrown(Exception e); +} // interface ExceptionListener diff --git a/libjava/classpath/java/beans/Expression.java b/libjava/classpath/java/beans/Expression.java new file mode 100644 index 000000000..d07c28b4a --- /dev/null +++ b/libjava/classpath/java/beans/Expression.java @@ -0,0 +1,138 @@ +/* java.beans.Expression + Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +/** + * <p>An Expression captures the execution of an object method + * that returns a value.</p> + * + * <p>It stores an object, the method to call, and the arguments to pass to + * the method.</p> + * + * <p>While this class can generally be used to describe method calls it is + * part of the XML serialization API.</p> + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class Expression extends Statement +{ + // This is a placeholder to indicate that value hasn't been set + // yet; + private static final Object UNSET = new Object(); + + // The value to return. This is equal to unset until getValue is called. + private Object value; + + /** + * Constructor Constructs an Expression representing the invocation of + * object.methodName(arg[0], arg[1], ...); However, it will never be executed. + * Instead, value will always be returned. + * + * @param value + * The value to return. + * @param target + * The object to invoke the method on. + * @param methodName + * The object method to invoke. + * @param arguments + * An array of arguments to pass to the method. + */ + public Expression(Object value, Object target, String methodName, + Object[] arguments) + { + super(target, methodName, arguments); + this.value = value; + } + + /** + * Constructor Constructs an Expression representing the invocation of + * object.methodName(arg[0], arg[1], ...); + * + * @param target + * The object to invoke the method on. + * @param methodName + * The object method to invoke. + * @param arguments + * An array of arguments to pass to the method. + */ + public Expression(Object target, String methodName, Object[] arguments) + { + super(target, methodName, arguments); + this.value = UNSET; + } + + /** + * Return the result of executing the method. If the cached value has not yet + * been set, the method is executed in the same way as Statement.execute(), + * except that the value is cached, and then returned. If the value has been + * set, it is returned without executing the method again. + * + * @return the result of executing the method. + * @exception Exception + * if an error occurs + */ + public Object getValue() throws Exception + { + if (value == UNSET) + value = doExecute(); + return value; + } + + /** + * Set the cached value to be returned by getValue() + * + * @param value + * the value to cache and return. + */ + public void setValue(Object value) + { + this.value = value; + } + + /** + * Return a string representation of this expression. + */ + public String toString() + { + String result = super.toString(); + if (value != UNSET) + return value.getClass().getName() + "=" + result; + return result; + } +} diff --git a/libjava/classpath/java/beans/FeatureDescriptor.java b/libjava/classpath/java/beans/FeatureDescriptor.java new file mode 100644 index 000000000..87e4a2fe7 --- /dev/null +++ b/libjava/classpath/java/beans/FeatureDescriptor.java @@ -0,0 +1,232 @@ +/* java.beans.FeatureDescriptor + Copyright (C) 1998, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * FeatureDescriptor is the common superclass for all JavaBeans Descriptor + * classes. JavaBeans descriptors are abstract descriptors of properties, + * events, methods, beans, etc.<P> + * + * <STRONG>Documentation Convention:</STRONG> for proper + * Internalization of Beans inside an RAD tool, sometimes there + * are two names for a property or method: a programmatic, or + * locale-independent name, which can be used anywhere, and a + * localized, display name, for ease of use. In the + * documentation I will specify different String values as + * either <EM>programmatic</EM> or <EM>localized</EM> to + * make this distinction clear. + * + * @author John Keiser + * @since 1.1 + */ + +public class FeatureDescriptor +{ + String name; + String displayName; + String shortDescription; + boolean expert; + boolean hidden; + boolean preferred; + + Hashtable<String,Object> valueHash; + + /** + * Instantiate this FeatureDescriptor with appropriate default values. + */ + public FeatureDescriptor() + { + valueHash = new Hashtable<String,Object>(); + } + + /** + * Get the programmatic name of this feature. + */ + public String getName() + { + return name; + } + + /** + * Set the programmatic name of this feature. + * + * @param name the new name for this feature. + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Get the localized (display) name of this feature. + * + * @returns The localized display name of this feature or falls + * back to the programmatic name. + */ + public String getDisplayName() + { + return (displayName == null) ? name : displayName; + } + + /** + * Set the localized (display) name of this feature. + * + * @param displayName the new display name for this feature. + */ + public void setDisplayName(String displayName) + { + this.displayName = displayName; + } + + /** + * Get the localized short description for this feature. + * + * @returns A short localized description of this feature or + * what <code>getDisplayName</code> returns in case, that no short description + * is available. + */ + public String getShortDescription() + { + return (shortDescription == null) ? getDisplayName() : shortDescription; + } + + /** + * Set the localized short description for this feature. + * + * @param shortDescription the new short description for this feature. + */ + public void setShortDescription(String shortDescription) + { + this.shortDescription = shortDescription; + } + + /** + * Indicates whether this feature is for expert use only. + * + * @return true if for use by experts only, + * or false if anyone can use it. + */ + public boolean isExpert() + { + return expert; + } + + /** + * Set whether this feature is for expert use only. + * + * @param expert true if for use by experts only, + * or false if anyone can use it. + */ + public void setExpert(boolean expert) + { + this.expert = expert; + } + + /** + * Indicates whether this feature is for use by tools only. + * If it is for use by tools only, then it should not be displayed. + * + * @return true if tools only should use it, + * or false if anyone can see it. + */ + public boolean isHidden() + { + return hidden; + } + + /** + * Set whether this feature is for use by tools only. + * If it is for use by tools only, then it should not be displayed. + * + * @param hidden true if tools only should use it, + * or false if anyone can see it. + */ + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public boolean isPreferred () + { + return preferred; + } + + public void setPreferred (boolean preferred) + { + this.preferred = preferred; + } + + /** + * Get an arbitrary value set with setValue(). + * + * @param name the programmatic name of the key. + * + * @return the value associated with this name, + * or null if there is none. + */ + public Object getValue(String name) + { + return valueHash.get(name); + } + + /** + * Set an arbitrary string-value pair with this feature. + * + * @param name the programmatic name of the key. + * @param value the value to associate with the name. + */ + public void setValue(String name, Object value) + { + valueHash.put(name, value); + } + + /** + * Get a list of the programmatic key names set with setValue(). + * + * @return an Enumerator over all the programmatic key names associated + * with this feature. + */ + public Enumeration<String> attributeNames() + { + return valueHash.keys(); + } +} diff --git a/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java new file mode 100644 index 000000000..9c46e1fa1 --- /dev/null +++ b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java @@ -0,0 +1,81 @@ +/* Indexed property change event + Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * This is like a PropertyChangeEvent, but also carries with it the + * index of the property which changed. + * @author Tom Tromey (tromey@redhat.com) + * @since 1.5 + */ +public class IndexedPropertyChangeEvent extends PropertyChangeEvent +{ + private static final long serialVersionUID = -320227448495806870L; + + /** + * Index of the item that was changed. + */ + private int index; + + /** + * Create a new IndexedPropertyChangeEvent. + * @param source the Bean containing the property + * @param name the property's name + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @param index the index of the element in the property which changed + * @throws IllegalArgumentException if source is null + */ + public IndexedPropertyChangeEvent(Object source, String name, + Object oldValue, Object newValue, + int index) + { + super(source, name, oldValue, newValue); + this.index = index; + } + + /** + * Return the index of the changed property. + * @return the index + */ + public int getIndex() + { + return index; + } +} diff --git a/libjava/classpath/java/beans/IndexedPropertyDescriptor.java b/libjava/classpath/java/beans/IndexedPropertyDescriptor.java new file mode 100644 index 000000000..b7914133a --- /dev/null +++ b/libjava/classpath/java/beans/IndexedPropertyDescriptor.java @@ -0,0 +1,421 @@ +/* IndexedPropertyDescriptor.java -- + Copyright (C) 1998, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * IndexedPropertyDescriptor describes information about a JavaBean + * indexed property, by which we mean an array-like property that + * has been exposed via a pair of get and set methods and another + * pair that allows you to get to the property by an index.<P> + * + * An example property would have four methods like this:<P> + * <CODE>FooBar[] getFoo()</CODE><BR> + * <CODE>void setFoo(FooBar[])</CODE><BR> + * <CODE>FooBar getFoo(int)</CODE><BR> + * <CODE>void setFoo(int,FooBar)</CODE><P> + * + * The constraints put on get and set methods are:<P> + * <OL> + * <LI>There must be at least a get(int) or a set(int,...) method. + * Nothing else is required. <B>Spec note:</B>One nice restriction + * would be that if there is a get() there must be a get(int), same + * with set, but that is not in the spec and is fairly harmless.)</LI> + * <LI>A get array method must have signature + * <CODE><propertyType>[] <getMethodName>()</CODE></LI> + * <LI>A set array method must have signature + * <CODE>void <setMethodName>(<propertyType>[])</CODE></LI> + * <LI>A get index method must have signature + * <CODE><propertyType> <getMethodName>(int)</CODE></LI> + * <LI>A set index method must have signature + * <CODE>void <setMethodName>(int,<propertyType>)</CODE></LI> + * <LI>All these methods may throw any exception.</LI> + * <LI>All these methods must be public.</LI> + * </OL> + * + * @author John Keiser + * @since JDK1.1 + */ +public class IndexedPropertyDescriptor extends PropertyDescriptor +{ + private Class<?> indexedPropertyType; + private Method setIndex; + private Method getIndex; + + /** + * Create a new IndexedPropertyDescriptor by introspection. + * This form of constructor creates the PropertyDescriptor by + * looking for getter methods named <CODE>get<name>()</CODE> + * and setter methods named + * <CODE>set<name>()</CODE> in class + * <CODE><beanClass></CODE>, where <name> has its + * first letter capitalized by the constructor.<P> + * + * <B>Implementation note:</B> If there is a get(int) method, + * then the return type of that method is used to find the + * remaining methods. If there is no get method, then the + * set(int) method is searched for exhaustively and that type + * is used to find the others.<P> + * + * <B>Spec note:</B> + * If there is no get(int) method and multiple set(int) methods with + * the same name and the correct parameters (different type of course), + * then an IntrospectionException is thrown. While Sun's spec + * does not state this, it can make Bean behavior different on + * different systems (since method order is not guaranteed) and as + * such, can be treated as a bug in the spec. I am not aware of + * whether Sun's implementation catches this. + * + * @param name the programmatic name of the property, usually + * starting with a lowercase letter (e.g. fooManChu + * instead of FooManChu). + * @param beanClass the class the get and set methods live in. + * + * @exception IntrospectionException if the methods are not found or + * invalid. + */ + public IndexedPropertyDescriptor(String name, Class<?> beanClass) + throws IntrospectionException + { + super(name); + String capitalized; + try + { + capitalized = Character.toUpperCase(name.charAt(0)) + + name.substring(1); + } + catch(StringIndexOutOfBoundsException e) + { + capitalized = ""; + } + findMethods(beanClass, "get" + capitalized, "set" + capitalized, + "get" + capitalized, "set" + capitalized); + } + + /** + * Create a new IndexedPropertyDescriptor by introspection. + * This form of constructor allows you to specify the + * names of the get and set methods to search for.<P> + * + * <B>Implementation note:</B> If there is a get(int) method, + * then the return type of that method is used to find the + * remaining methods. If there is no get method, then the + * set(int) method is searched for exhaustively and that type + * is used to find the others.<P> + * + * <B>Spec note:</B> + * If there is no get(int) method and multiple set(int) methods with + * the same name and the correct parameters (different type of course), + * then an IntrospectionException is thrown. While Sun's spec + * does not state this, it can make Bean behavior different on + * different systems (since method order is not guaranteed) and as + * such, can be treated as a bug in the spec. I am not aware of + * whether Sun's implementation catches this. + * + * @param name the programmatic name of the property, usually + * starting with a lowercase letter (e.g. fooManChu + * instead of FooManChu). + * @param beanClass the class the get and set methods live in. + * @param getMethodName the name of the get array method. + * @param setMethodName the name of the set array method. + * @param getIndexName the name of the get index method. + * @param setIndexName the name of the set index method. + * + * @exception IntrospectionException if the methods are not found or invalid. + */ + public IndexedPropertyDescriptor(String name, Class<?> beanClass, + String getMethodName, String setMethodName, + String getIndexName, String setIndexName) + throws IntrospectionException + { + super(name); + findMethods(beanClass, getMethodName, setMethodName, getIndexName, + setIndexName); + } + + /** + * Create a new PropertyDescriptor using explicit Methods. + * Note that the methods will be checked for conformance to standard + * Property method rules, as described above at the top of this class. + * + * @param name the programmatic name of the property, usually + * starting with a lowercase letter (e.g. fooManChu + * instead of FooManChu). + * @param getMethod the get array method. + * @param setMethod the set array method. + * @param getIndex the get index method. + * @param setIndex the set index method. + * + * @exception IntrospectionException if the methods are not found or invalid. + */ + public IndexedPropertyDescriptor(String name, Method getMethod, + Method setMethod, Method getIndex, + Method setIndex) + throws IntrospectionException + { + super(name); + if(getMethod != null && getMethod.getParameterTypes().length > 0) + throw new IntrospectionException("get method has parameters"); + if(getMethod != null && setMethod.getParameterTypes().length != 1) + throw new IntrospectionException("set method does not have exactly one parameter"); + if(getMethod != null && setMethod != null) + { + if(!getMethod.getReturnType().equals(setMethod.getParameterTypes()[0])) + { + throw new IntrospectionException("set and get methods do not " + + "share the same type"); + } + if(!getMethod.getDeclaringClass().isAssignableFrom + (setMethod.getDeclaringClass()) + && !setMethod.getDeclaringClass().isAssignableFrom + (getMethod.getDeclaringClass())) + { + throw new IntrospectionException("set and get methods are not in " + + "the same class."); + } + } + + if (getIndex != null + && (getIndex.getParameterTypes().length != 1 + || !(getIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE))) + { + throw new IntrospectionException("get index method has wrong " + + "parameters"); + } + if (setIndex != null + && (setIndex.getParameterTypes().length != 2 + || !(setIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE))) + { + throw new IntrospectionException("set index method has wrong " + + "parameters"); + } + if (getIndex != null && setIndex != null) + { + if(!getIndex.getReturnType().equals(setIndex.getParameterTypes()[1])) + { + throw new IntrospectionException("set index methods do not share " + + "the same type"); + } + if(!getIndex.getDeclaringClass().isAssignableFrom + (setIndex.getDeclaringClass()) + && !setIndex.getDeclaringClass().isAssignableFrom + (getIndex.getDeclaringClass())) + { + throw new IntrospectionException("get and set index methods are " + + "not in the same class."); + } + } + + if (getIndex != null && getMethod != null + && !getIndex.getDeclaringClass().isAssignableFrom + (getMethod.getDeclaringClass()) + && !getMethod.getDeclaringClass().isAssignableFrom + (getIndex.getDeclaringClass())) + { + throw new IntrospectionException("methods are not in the same class."); + } + + if (getIndex != null && getMethod != null + && !Array.newInstance(getIndex.getReturnType(),0) + .getClass().equals(getMethod.getReturnType())) + { + throw new IntrospectionException("array methods do not match index " + + "methods."); + } + + this.getMethod = getMethod; + this.setMethod = setMethod; + this.getIndex = getIndex; + this.setIndex = setIndex; + this.indexedPropertyType = getIndex != null ? getIndex.getReturnType() + : setIndex.getParameterTypes()[1]; + this.propertyType = getMethod != null ? getMethod.getReturnType() + : (setMethod != null ? setMethod.getParameterTypes()[0] + : Array.newInstance(this.indexedPropertyType,0).getClass()); + } + + public Class<?> getIndexedPropertyType() + { + return indexedPropertyType; + } + + public Method getIndexedReadMethod() + { + return getIndex; + } + + /** + * Sets the method that is used to read an indexed property. + * + * @param m the method to set + */ + public void setIndexedReadMethod(Method m) throws IntrospectionException + { + getIndex = m; + } + + public Method getIndexedWriteMethod() + { + return setIndex; + } + + /** + * Sets the method that is used to write an indexed property. + * + * @param m the method to set + */ + public void setIndexedWriteMethod(Method m) throws IntrospectionException + { + setIndex = m; + } + + private void findMethods(Class beanClass, String getMethodName, + String setMethodName, String getIndexName, + String setIndexName) + throws IntrospectionException + { + try + { + if(getIndexName != null) + { + try + { + Class[] getArgs = new Class[1]; + getArgs[0] = java.lang.Integer.TYPE; + getIndex = beanClass.getMethod(getIndexName,getArgs); + indexedPropertyType = getIndex.getReturnType(); + } + catch(NoSuchMethodException E) + { + } + } + if(getIndex != null) + { + if(setIndexName != null) + { + try + { + Class[] setArgs = new Class[2]; + setArgs[0] = java.lang.Integer.TYPE; + setArgs[1] = indexedPropertyType; + setIndex = beanClass.getMethod(setIndexName,setArgs); + if(!setIndex.getReturnType().equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException(setIndexName + + " has non-void return type"); + } + } + catch(NoSuchMethodException E) + { + } + } + } + else if(setIndexName != null) + { + Method[] m = beanClass.getMethods(); + for(int i=0;i<m.length;i++) + { + Method current = m[i]; + if(current.getName().equals(setIndexName) + && current.getParameterTypes().length == 2 + && (current.getParameterTypes()[0]) + .equals(java.lang.Integer.TYPE) + && current.getReturnType().equals(java.lang.Void.TYPE)) + { + if(setIndex != null) + { + throw new IntrospectionException("Multiple, different " + + "set methods found that fit the bill!"); + } + else + { + setIndex = current; + indexedPropertyType = current.getParameterTypes()[1]; + } + } + } + if(setIndex == null) + { + throw new IntrospectionException("Cannot find get or set " + + "methods."); + } + } + else + { + throw new IntrospectionException("Cannot find get or set methods."); + } + + Class arrayType = Array.newInstance(indexedPropertyType,0).getClass(); + + Class[] setArgs = new Class[1]; + setArgs[0] = arrayType; + try + { + setMethod = beanClass.getMethod(setMethodName,setArgs); + if (!setMethod.getReturnType().equals(java.lang.Void.TYPE)) + { + setMethod = null; + } + } + catch(NoSuchMethodException E) + { + } + + Class[] getArgs = new Class[0]; + try + { + getMethod = beanClass.getMethod(getMethodName,getArgs); + if (!getMethod.getReturnType().equals(arrayType)) + { + getMethod = null; + } + } + catch(NoSuchMethodException E) + { + } + } + catch(SecurityException E) + { + throw new IntrospectionException("SecurityException while trying to " + + "find methods."); + } + } +} diff --git a/libjava/classpath/java/beans/IntrospectionException.java b/libjava/classpath/java/beans/IntrospectionException.java new file mode 100644 index 000000000..7bb83dac5 --- /dev/null +++ b/libjava/classpath/java/beans/IntrospectionException.java @@ -0,0 +1,67 @@ +/* IntrospectionException -- thrown when an exception occurs in introspection + Copyright (C) 1998, 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * IntrospectionException is thrown when the Introspector fails. Typical + * causes are the inability to map a name to its Class, or specifying a + * wrong type signature. + * + * @author John Keiser + * @see Introspector + * @since 1.1 + * @status updated to 1.4 + */ +public class IntrospectionException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3728150539969542619L; + + /** + * Instantiate this exception with the given message. + * + * @param msg the message for the exception + */ + public IntrospectionException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/beans/Introspector.java b/libjava/classpath/java/beans/Introspector.java new file mode 100644 index 000000000..76412e022 --- /dev/null +++ b/libjava/classpath/java/beans/Introspector.java @@ -0,0 +1,705 @@ +/* java.beans.Introspector + Copyright (C) 1998, 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.BeanInfoEmbryo; +import gnu.java.beans.ExplicitBeanInfo; +import gnu.java.beans.IntrospectionIncubator; +import gnu.java.lang.ClassHelper; + +import java.util.Hashtable; +import java.util.Vector; + +/** + * 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.</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.</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 + * @see java.beans.BeanInfo + */ +public class Introspector { + + public static final int USE_ALL_BEANINFO = 1; + public static final int IGNORE_IMMEDIATE_BEANINFO = 2; + public static final int IGNORE_ALL_BEANINFO = 3; + + static String[] beanInfoSearchPath = {"gnu.java.beans.info"}; + static Hashtable<Class<?>,BeanInfo> beanInfoCache = + new Hashtable<Class<?>,BeanInfo>(); + + 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 = beanInfoCache.get(beanClass); + if(cachedInfo != null) + { + return cachedInfo; + } + cachedInfo = getBeanInfo(beanClass,null); + beanInfoCache.put(beanClass,cachedInfo); + return cachedInfo; + } + } + + /** + * Returns a {@BeanInfo} instance for the given Bean class where a flag + * controls the usage of explicit BeanInfo class to retrieve that + * information. + * + * <p>You have three options:</p> + * <p>With {@link #USE_ALL_BEANINFO} the result is the same as + * {@link #getBeanInfo(Class)}.</p> + * + * <p>Calling the method with <code>flag</code> set to + * {@link #IGNORE_IMMEDIATE_BEANINFO} will let it use all + * explicit BeanInfo classes for the beans superclasses + * but not for the bean class itself. Furthermore eventset, + * property and method information is retrieved by introspection + * if the explicit <code>BeanInfos</code> did not provide such data + * (ie. return <code>null</code> on {@link BeanInfo.getMethodDescriptors}, + * {@link BeanInfo.getEventSetDescriptors} and + * {@link BeanInfo.getPropertyDescriptors}.) + * </p> + * + * <p>When the method is called with <code>flag</code< set to + * {@link #IGNORE_ALL_BEANINFO} all the bean data is retrieved + * by inspecting the class.</p> + * + * <p>Note: Any unknown value for <code>flag</code> is interpreted + * as {@link #IGNORE_ALL_BEANINFO}</p>. + * + * @param beanClass The class whose BeanInfo should be returned. + * @param flag Controls the usage of explicit <code>BeanInfo</code> classes. + * @return A BeanInfo object describing the class. + * @throws IntrospectionException If something goes wrong while retrieving + * the bean data. + */ + public static BeanInfo getBeanInfo(Class<?> beanClass, int flag) + throws IntrospectionException + { + IntrospectionIncubator ii; + BeanInfoEmbryo infoEmbryo; + + switch(flag) + { + case USE_ALL_BEANINFO: + return getBeanInfo(beanClass); + case IGNORE_IMMEDIATE_BEANINFO: + Class superclass = beanClass.getSuperclass(); + ExplicitInfo explicit = new ExplicitInfo(superclass, null); + + ii = new IntrospectionIncubator(); + if (explicit.explicitEventSetDescriptors != null) + ii.setEventStopClass(superclass); + + if (explicit.explicitMethodDescriptors != null) + ii.setMethodStopClass(superclass); + + if (explicit.explicitPropertyDescriptors != null) + ii.setPropertyStopClass(superclass); + + ii.addMethods(beanClass.getMethods()); + + infoEmbryo = ii.getBeanInfoEmbryo(); + merge(infoEmbryo, explicit); + + infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null)); + + return infoEmbryo.getBeanInfo(); + case IGNORE_ALL_BEANINFO: + default: + ii = new IntrospectionIncubator(); + ii.addMethods(beanClass.getMethods()); + infoEmbryo = ii.getBeanInfoEmbryo(); + infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null)); + + return infoEmbryo.getBeanInfo(); + } + } + + /** + * Flush all of the Introspector's internal caches. + * + * @since 1.2 + */ + public static void flushCaches() + { + beanInfoCache.clear(); + + // Clears all the intermediate ExplicitInfo instances which + // have been created. + // This makes sure we have to retrieve stuff like BeanDescriptors + // again. (Remember that FeatureDescriptor can be modified by the user.) + ExplicitInfo.flushCaches(); + } + + /** + * Flush the Introspector's internal cached information for a given + * class. + * + * @param clz the class to be flushed. + * @throws NullPointerException if clz is null. + * @since 1.2 + */ + public static void flushFromCaches(Class<?> clz) + { + synchronized (clz) + { + beanInfoCache.remove(clz); + } + } + + /** Adds all explicity given bean info data to the introspected + * data. + * + * @param infoEmbryo Bean info data retrieved by introspection. + * @param explicit Bean info data retrieved by BeanInfo classes. + */ + private static void merge(BeanInfoEmbryo infoEmbryo, ExplicitInfo explicit) + { + PropertyDescriptor[] p = explicit.explicitPropertyDescriptors; + if(p!=null) + { + for(int i=0;i<p.length;i++) + { + if(!infoEmbryo.hasProperty(p[i])) + { + infoEmbryo.addProperty(p[i]); + } + } + + // -1 should be used to denote a missing default property but + // for robustness reasons any value below zero is discarded. + // Not doing so would let Classpath fail where the JDK succeeds. + if(explicit.defaultProperty > -1) + { + infoEmbryo.setDefaultPropertyName(p[explicit.defaultProperty].getName()); + } + } + EventSetDescriptor[] e = explicit.explicitEventSetDescriptors; + if(e!=null) + { + for(int i=0;i<e.length;i++) + { + if(!infoEmbryo.hasEvent(e[i])) + { + infoEmbryo.addEvent(e[i]); + } + } + + // -1 should be used to denote a missing default event but + // for robustness reasons any value below zero is discarded. + // Not doing so would let Classpath fail where the JDK succeeds. + if(explicit.defaultEvent > -1) + { + infoEmbryo.setDefaultEventName(e[explicit.defaultEvent].getName()); + } + } + MethodDescriptor[] m = explicit.explicitMethodDescriptors; + if(m!=null) + { + for(int i=0;i<m.length;i++) + { + if(!infoEmbryo.hasMethod(m[i])) + { + infoEmbryo.addMethod(m[i]); + } + } + } + + infoEmbryo.setAdditionalBeanInfo(explicit.explicitBeanInfo); + infoEmbryo.setIcons(explicit.im); + + } + + /** + * 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(); + + merge(currentInfo, explicit); + + // Sets the info's BeanDescriptor to the one we extracted from the + // explicit BeanInfo instance(s) if they contained one. Otherwise we + // create the BeanDescriptor from scratch. + // Note: We do not create a copy the retrieved BeanDescriptor which will allow + // the user to modify the instance while it is cached. However this is how + // the RI does it. + currentInfo.setBeanDescriptor( + (explicit.explicitBeanDescriptor == null ? + new BeanDescriptor(beanClass, null) : + explicit.explicitBeanDescriptor)); + 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; + + static Hashtable explicitBeanInfos = new Hashtable(); + static Vector emptyBeanInfos = new Vector(); + + 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; + } + } + + /** Throws away all cached data and makes sure we re-instantiate things + * like BeanDescriptors again. + */ + static void flushCaches() { + explicitBeanInfos.clear(); + emptyBeanInfos.clear(); + } + + 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) + { + ClassLoader beanClassLoader = beanClass.getClassLoader(); + BeanInfo beanInfo; + + beanInfo = getBeanInfo(beanClassLoader, beanClass.getName() + "BeanInfo"); + if (beanInfo == null) + { + String newName; + newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo"; + + for(int i = 0; i < Introspector.beanInfoSearchPath.length; i++) + { + if (Introspector.beanInfoSearchPath[i].equals("")) + beanInfo = getBeanInfo(beanClassLoader, newName); + else + beanInfo = getBeanInfo(beanClassLoader, + Introspector.beanInfoSearchPath[i] + "." + + newName); + + // Returns the beanInfo if it exists and the described class matches + // the one we searched. + if (beanInfo != null && beanInfo.getBeanDescriptor() != null && + beanInfo.getBeanDescriptor().getBeanClass() == beanClass) + + return beanInfo; + } + } + + return beanInfo; + } + + /** + * Returns an instance of the given class name when it can be loaded + * through the given class loader, or null otherwise. + */ + private static BeanInfo getBeanInfo(ClassLoader cl, String infoName) + { + try + { + return (BeanInfo) Class.forName(infoName, true, cl).newInstance(); + } + catch (ClassNotFoundException cnfe) + { + return null; + } + catch (IllegalAccessException iae) + { + return null; + } + catch (InstantiationException ie) + { + return null; + } + } + +} diff --git a/libjava/classpath/java/beans/MethodDescriptor.java b/libjava/classpath/java/beans/MethodDescriptor.java new file mode 100644 index 000000000..0185fde81 --- /dev/null +++ b/libjava/classpath/java/beans/MethodDescriptor.java @@ -0,0 +1,87 @@ +/* java.beans.MethodDescriptor + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.Method; + +/** MethodDescriptor describes information about a JavaBeans method. + ** It's a fairly straightforward class (at least something in this + ** package is straightforward!). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 26 Jul 1998 + **/ +public class MethodDescriptor extends FeatureDescriptor { + private Method m; + private ParameterDescriptor[] parameterDescriptors; + + /** Create a new MethodDescriptor. + ** This method sets the name to the name of the method (Method.getName()). + ** @param m the method it will represent. + **/ + public MethodDescriptor(Method m) { + setName(m.getName()); + this.m = m; + } + + /** Create a new MethodDescriptor. + ** This method sets the name to the name of the method (Method.getName()). + ** @param m the method it will represent. + ** @param parameterDescriptors descriptions of the parameters (especially names). + **/ + public MethodDescriptor(Method m, ParameterDescriptor[] parameterDescriptors) { + setName(m.getName()); + this.m = m; + this.parameterDescriptors = parameterDescriptors; + } + + /** Get the parameter descriptors from this method. + ** Since MethodDescriptor has no way of determining what + ** the parameter names were, this defaults to null. + **/ + public ParameterDescriptor[] getParameterDescriptors() { + return parameterDescriptors; + } + + /** Get the method this MethodDescriptor represents. **/ + public Method getMethod() { + return m; + } +} diff --git a/libjava/classpath/java/beans/ParameterDescriptor.java b/libjava/classpath/java/beans/ParameterDescriptor.java new file mode 100644 index 000000000..148690066 --- /dev/null +++ b/libjava/classpath/java/beans/ParameterDescriptor.java @@ -0,0 +1,52 @@ +/* java.beans.MethodDescriptor + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** ParameterDescriptor represents a single parameter to a method. + ** As it turns out, FeatureDescriptor is sufficient to hold all + ** the information. Use its constructor and methods to set + ** the appropriate values. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 26 Jul 1998 + **/ +public class ParameterDescriptor extends FeatureDescriptor { + +} diff --git a/libjava/classpath/java/beans/PersistenceDelegate.java b/libjava/classpath/java/beans/PersistenceDelegate.java new file mode 100644 index 000000000..d4e777844 --- /dev/null +++ b/libjava/classpath/java/beans/PersistenceDelegate.java @@ -0,0 +1,90 @@ +/* java.beans.PersistenceDelegate + Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +/** <p>A <code>PersistenceDelegate</code> describes how a another object + * has to constructed and transformed in order to create a complete + * replicate.</p> + * + * <p>For custom classes you will need to implement + * <code>PersistenceDelegate</code> in a way that is suitable for them. + * To make use of the implementation you have to register it with an + * {@link Encoder} using the {Encoder#setPersistenceDelegate} method.</p> + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public abstract class PersistenceDelegate +{ + + protected void initialize(Class<?> type, Object oldInstance, + Object newInstance, Encoder out) + { + if (type != Object.class) + { + type = type.getSuperclass(); + + PersistenceDelegate pd = out.getPersistenceDelegate(type); + + pd.initialize(type, oldInstance, newInstance, out); + } + } + + public void writeObject(Object oldInstance, Encoder out) + { + Object streamCandidate = out.get(oldInstance); + + if (mutatesTo(oldInstance, streamCandidate)) + { + initialize(oldInstance.getClass(), oldInstance, streamCandidate, out); + } + else + { + out.remove(oldInstance); + out.writeExpression(instantiate(oldInstance, out)); + } + } + + protected boolean mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null) + && oldInstance.getClass() == newInstance.getClass(); + } + + protected abstract Expression instantiate(Object oldInstance, Encoder out); +} diff --git a/libjava/classpath/java/beans/PropertyChangeEvent.java b/libjava/classpath/java/beans/PropertyChangeEvent.java new file mode 100644 index 000000000..3e3f948e3 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeEvent.java @@ -0,0 +1,189 @@ +/* PropertyChangeEvent.java -- describes a change in a property + Copyright (C) 1998, 2000, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventObject; + +/** + * PropertyChangeEvents are fired in the PropertyChange and VetoableChange + * event classes. They represent the old and new values as well as the + * source Bean. If the old or new value is a primitive type, it must be + * wrapped in the appropriate wrapper type (java.lang.Integer for int, etc., + * etc.). + * + * <p>If the old or new values are unknown (although why that would be I do + * not know), they may be null. Also, if the set of properties itself has + * changed, the name should be null, and the old and new values may also be + * null. Right now Sun put in a propagationId, reserved for future use. Read + * the comments on the constructor and on setPropagationId for more + * information. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status udpated to 1.4 + */ +public class PropertyChangeEvent extends EventObject +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7042693688939648123L; + + /** + * The name of the property that changed, may be null. Package visible for + * use by PropertyChangeSupport. + * + * @serial the changed property name + */ + final String propertyName; + + /** + * The new value of the property, may be null. Package visible for use by + * PropertyChangeSupport. + * + * @serial the new property value + */ + final Object newValue; + + /** + * The old value of the property, may be null. Package visible for use by + * PropertyChangeSupport. + * + * @serial the old property value + */ + final Object oldValue; + + /** + * The propagation ID, reserved for future use. May be null. + * + * @see #getPropagationId() + * @serial the Propagation ID + */ + private Object propagationId; + + /** + * Create a new PropertyChangeEvent. Remember that if you received a + * PropertyChangeEvent and are sending a new one, you should also set the + * propagation ID from the old PropertyChangeEvent. + * + * @param source the Bean containing the property + * @param propertyName the property's name + * @param oldVal the old value of the property + * @param newVal the new value of the property + * @throws IllegalArgumentException if source is null + */ + public PropertyChangeEvent(Object source, String propertyName, + Object oldVal, Object newVal) + { + super(source); + this.propertyName = propertyName; + oldValue = oldVal; + newValue = newVal; + } + + /** + * Get the property name. May be null if multiple properties changed. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } + + /** + * Get the property's new value. May be null if multiple properties changed. + * + * @return the property's new value + */ + public Object getNewValue() + { + return newValue; + } + + /** + * Get the property's old value. May be null if multiple properties changed. + * + * @return the property's old value + */ + public Object getOldValue() + { + return oldValue; + } + + /** + * Set the propagation ID. This is a way for the event to be passed from + * hand to hand and retain a little extra state. Right now it is unused, + * but it should be propagated anyway so that future versions of JavaBeans + * can use it, for God knows what. + * + * @param propagationId the propagation ID + * @see #getPropagationId() + */ + public void setPropagationId(Object propagationId) + { + this.propagationId = propagationId; + } + + /** + * Get the propagation ID. Right now, it is not used for anything. + * + * @return the propagation ID + * @see #setPropagationId(Object) + */ + public Object getPropagationId() + { + return propagationId; + } + + /** + * Utility method to rollback a change. + * + * @param event the event to rollback + * @return a new event with old and new swapped + */ + PropertyChangeEvent rollback() + { + PropertyChangeEvent result + = new PropertyChangeEvent(source, propertyName, newValue, oldValue); + result.propagationId = propagationId; + return result; + } +} // class PropertyChangeEvent diff --git a/libjava/classpath/java/beans/PropertyChangeListener.java b/libjava/classpath/java/beans/PropertyChangeListener.java new file mode 100644 index 000000000..a97e6e1c2 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeListener.java @@ -0,0 +1,61 @@ +/* PropertyChangeListener.java -- listen for changes in a bound property + Copyright (C) 1998, 2000, 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListener; + +/** + * PropertyChangeListener allows a class to monitor properties of a Bean for + * changes. A propertyChange() event will only be fired <em>after</em> the + * property has changed. + * + * @author John Keiser + * @see PropertyChangeSupport + * @since 1.1 + * @status updated to 1.4 + */ +public interface PropertyChangeListener extends EventListener +{ + /** + * Fired after a Bean's property has changed. + * + * @param e the change (containing the old and new values) + */ + void propertyChange(PropertyChangeEvent e); +} // interface PropertyChangeListener diff --git a/libjava/classpath/java/beans/PropertyChangeListenerProxy.java b/libjava/classpath/java/beans/PropertyChangeListenerProxy.java new file mode 100644 index 000000000..68a815303 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeListenerProxy.java @@ -0,0 +1,102 @@ +/* PropertyChangeListenerProxy.java -- adds a name to a property listener + Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * This class provides an extension to <code>PropertyChangeListener</code> - + * associating a name with the listener. This can be used to filter the + * changes that one is interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status udpated to 1.4 + */ +public class PropertyChangeListenerProxy extends EventListenerProxy + implements PropertyChangeListener +{ + /** + * The name of the property to listen for. Package visible for use by + * PropertyChangeSupport. + */ + final String propertyName; + + /** + * Create a new proxy which filters property change events and only passes + * changes to the named property on to the delegate. A null propertyName + * or listener does not fail now, but may cause a NullPointerException down + * the road. + * + * @param propertyName the property's name to filter on + * @param listener the delegate listener + */ + public PropertyChangeListenerProxy(String propertyName, + PropertyChangeListener listener) + { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the event on to the delegate if the property name matches. + * + * @param event the event to pass on, if it meets the filter + * @throws NullPointerException if the delegate this was created with is null + */ + public void propertyChange(PropertyChangeEvent event) + { + // Note: Sun does not filter, under the assumption that since + // PropertyChangeSupport unwraps proxys, this method should never be + // called by normal use of listeners. + String name = event == null ? null : event.getPropertyName(); + if (name == null ? propertyName == null : name.equals(propertyName)) + ((PropertyChangeListener) getListener()).propertyChange(event); + } + + /** + * Gets the name of the property this proxy is filtering on. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } +} // class PropertyChangeListenerProxy diff --git a/libjava/classpath/java/beans/PropertyChangeSupport.java b/libjava/classpath/java/beans/PropertyChangeSupport.java new file mode 100644 index 000000000..3f3f53d42 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeSupport.java @@ -0,0 +1,545 @@ +/* PropertyChangeSupport.java -- support to manage property change listeners + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006 + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Vector; + +/** + * PropertyChangeSupport makes it easy to fire property change events and + * handle listeners. It allows chaining of listeners, as well as filtering + * by property name. In addition, it will serialize only those listeners + * which are serializable, ignoring the others without problem. This class + * is thread-safe. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyChangeSupport implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6401253773779951803L; + + /** + * Maps property names (String) to named listeners (PropertyChangeSupport). + * If this is a child instance, this field will be null. + * + * @serial the map of property names to named listener managers + * @since 1.2 + */ + private Hashtable children; + + /** + * The non-null source object for any generated events. + * + * @serial the event source + */ + private final Object source; + + /** + * A field to compare serialization versions - this class uses version 2. + * + * @serial the serialization format + */ + private static final int propertyChangeSupportSerializedDataVersion = 2; + + /** + * The list of all registered property listeners. If this instance was + * created by user code, this only holds the global listeners (ie. not tied + * to a name), and may be null. If it was created by this class, as a + * helper for named properties, then this vector will be non-null, and this + * instance appears as a value in the <code>children</code> hashtable of + * another instance, so that the listeners are tied to the key of that + * hashtable entry. + */ + private transient Vector listeners; + + /** + * Create a PropertyChangeSupport to work with a specific source bean. + * + * @param source the source bean to use + * @throws NullPointerException if source is null + */ + public PropertyChangeSupport(Object source) + { + this.source = source; + if (source == null) + throw new NullPointerException(); + } + + /** + * Adds a PropertyChangeListener to the list of global listeners. All + * property change events will be sent to this listener. The listener add + * is not unique: that is, <em>n</em> adds with the same listener will + * result in <em>n</em> events being sent to that listener for every + * property change. Adding a null listener is silently ignored. + * This method will unwrap a PropertyChangeListenerProxy, + * registering the underlying delegate to the named property list. + * + * @param l the listener to add + */ + public synchronized void addPropertyChangeListener(PropertyChangeListener l) + { + if (l == null) + return; + + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + addPropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else + { + if (listeners == null) + listeners = new Vector(); + listeners.add(l); + } + } + + /** + * Removes a PropertyChangeListener from the list of global listeners. If + * any specific properties are being listened on, they must be deregistered + * by themselves; this will only remove the general listener to all + * properties. If <code>add()</code> has been called multiple times for a + * particular listener, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list. + * + * @param l the listener to remove + */ + public synchronized void + removePropertyChangeListener(PropertyChangeListener l) + { + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + removePropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else if (listeners != null) + { + listeners.remove(l); + if (listeners.isEmpty()) + listeners = null; + } + } + + /** + * Returns an array of all registered property change listeners. Those that + * were registered under a name will be wrapped in a + * <code>PropertyChangeListenerProxy</code>, so you must check whether the + * listener is an instance of the proxy class in order to see what name the + * real listener is registered under. If there are no registered listeners, + * this returns an empty array. + * + * @return the array of registered listeners + * @see PropertyChangeListenerProxy + * @since 1.4 + */ + public synchronized PropertyChangeListener[] getPropertyChangeListeners() + { + ArrayList list = new ArrayList(); + if (listeners != null) + list.addAll(listeners); + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + Vector v = ((PropertyChangeSupport) e.getValue()).listeners; + int j = v.size(); + while (--j >= 0) + list.add(new PropertyChangeListenerProxy + (name, (PropertyChangeListener) v.get(j))); + } + } + return (PropertyChangeListener[]) + list.toArray(new PropertyChangeListener[list.size()]); + } + + /** + * Adds a PropertyChangeListener listening on the specified property. Events + * will be sent to the listener only if the property name matches. The + * listener add is not unique; that is, <em>n</em> adds on a particular + * property for a particular listener will result in <em>n</em> events + * being sent to that listener when that property is changed. The effect is + * cumulative, too; if you are registered to listen to receive events on + * all property changes, and then you register on a particular property, + * you will receive change events for that property twice. Adding a null + * listener is silently ignored. This method will unwrap a + * PropertyChangeListenerProxy, registering the underlying + * delegate to the named property list if the names match, and discarding + * it otherwise. + * + * @param propertyName the name of the property to listen on + * @param l the listener to add + * @throws NullPointerException if propertyName is null + */ + public synchronized void addPropertyChangeListener(String propertyName, + PropertyChangeListener l) + { + if (l == null) + return; + + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + PropertyChangeSupport s = null; + if (children == null) + children = new Hashtable(); + else + s = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + { + s = new PropertyChangeSupport(source); + s.listeners = new Vector(); + children.put(propertyName, s); + } + s.listeners.add(l); + } + + /** + * Removes a PropertyChangeListener from listening to a specific property. + * If <code>add()</code> has been called multiple times for a particular + * listener on a property, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list if the names match. + * + * @param propertyName the property to stop listening on + * @param l the listener to remove + * @throws NullPointerException if propertyName is null + */ + public synchronized void + removePropertyChangeListener(String propertyName, PropertyChangeListener l) + { + if (children == null) + return; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return; + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + s.listeners.remove(l); + if (s.listeners.isEmpty()) + { + children.remove(propertyName); + if (children.isEmpty()) + children = null; + } + } + + /** + * Returns an array of all property change listeners registered under the + * given property name. If there are no registered listeners, or + * propertyName is null, this returns an empty array. + * + * @return the array of registered listeners + * @since 1.4 + */ + public synchronized PropertyChangeListener[] + getPropertyChangeListeners(String propertyName) + { + if (children == null || propertyName == null) + return new PropertyChangeListener[0]; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return new PropertyChangeListener[0]; + return (PropertyChangeListener[]) + s.listeners.toArray(new PropertyChangeListener[s.listeners.size()]); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are non-null + * and equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + Object oldVal, Object newVal) + { + firePropertyChange(new PropertyChangeEvent(source, propertyName, + oldVal, newVal)); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, int oldVal, int newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + Integer.valueOf(oldVal), + Integer.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + boolean oldVal, boolean newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + Boolean.valueOf(oldVal), + Boolean.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent to all the global listeners, and to all the + * listeners for the specified property name. This does nothing if old and + * new values of the event are equal. + * + * @param event the event to fire + * @throws NullPointerException if event is null + */ + public void firePropertyChange(PropertyChangeEvent event) + { + if (event.oldValue != null && event.oldValue.equals(event.newValue)) + return; + Vector v = listeners; // Be thread-safe. + if (v != null) + { + int i = v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + Hashtable h = children; // Be thread-safe. + if (h != null && event.propertyName != null) + { + PropertyChangeSupport s + = (PropertyChangeSupport) h.get(event.propertyName); + if (s != null) + { + v = s.listeners; // Be thread-safe. + int i = v == null ? 0 : v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + } + } + + /** + * Fire an indexed property change event. This will only fire + * an event if the old and new values are not equal and not null. + * @param name the name of the property which changed + * @param index the index of the property which changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String name, int index, + Object oldValue, Object newValue) + { + // Argument checking is done in firePropertyChange(PropertyChangeEvent) . + firePropertyChange(new IndexedPropertyChangeEvent(source, name, + oldValue, newValue, + index)); + } + + /** + * Fire an indexed property change event. This will only fire + * an event if the old and new values are not equal. + * @param name the name of the property which changed + * @param index the index of the property which changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String name, int index, + int oldValue, int newValue) + { + if (oldValue != newValue) + fireIndexedPropertyChange(name, index, Integer.valueOf(oldValue), + Integer.valueOf(newValue)); + } + + /** + * Fire an indexed property change event. This will only fire + * an event if the old and new values are not equal. + * @param name the name of the property which changed + * @param index the index of the property which changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String name, int index, + boolean oldValue, boolean newValue) + { + if (oldValue != newValue) + fireIndexedPropertyChange(name, index, Boolean.valueOf(oldValue), + Boolean.valueOf(newValue)); + } + + /** + * Tell whether the specified property is being listened on or not. This + * will only return <code>true</code> if there are listeners on all + * properties or if there is a listener specifically on this property. + * + * @param propertyName the property that may be listened on + * @return whether the property is being listened on + */ + public synchronized boolean hasListeners(String propertyName) + { + return listeners != null || (children != null + && children.get(propertyName) != null); + } + + /** + * Saves the state of the object to the stream. + * + * @param s the stream to write to + * @throws IOException if anything goes wrong + * @serialData this writes out a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + if (listeners != null) + { + int i = listeners.size(); + while (--i >= 0) + if (listeners.get(i) instanceof Serializable) + s.writeObject(listeners.get(i)); + } + s.writeObject(null); + } + + /** + * Reads the object back from stream (deserialization). + * + * XXX Since serialization for 1.1 streams was not documented, this may + * not work if propertyChangeSupportSerializedDataVersion is 1. + * + * @param s the stream to read from + * @throws IOException if reading the stream fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads in a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + PropertyChangeListener l = (PropertyChangeListener) s.readObject(); + while (l != null) + { + addPropertyChangeListener(l); + l = (PropertyChangeListener) s.readObject(); + } + // Sun is not as careful with children as we are, and lets some proxys + // in that can never receive events. So, we clean up anything that got + // serialized, to make sure our invariants hold. + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + PropertyChangeSupport pcs = (PropertyChangeSupport) e.getValue(); + if (pcs.listeners == null) + pcs.listeners = new Vector(); + if (pcs.children != null) + pcs.listeners.addAll + (Arrays.asList(pcs.getPropertyChangeListeners(name))); + if (pcs.listeners.size() == 0) + iter.remove(); + else + pcs.children = null; + } + if (children.size() == 0) + children = null; + } + } +} // class PropertyChangeSupport diff --git a/libjava/classpath/java/beans/PropertyDescriptor.java b/libjava/classpath/java/beans/PropertyDescriptor.java new file mode 100644 index 000000000..a74fa7b13 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyDescriptor.java @@ -0,0 +1,665 @@ +/* java.beans.PropertyDescriptor + Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + ** PropertyDescriptor describes information about a JavaBean property, + ** by which we mean a property that has been exposed via a pair of + ** get and set methods. (There may be no get method, which means + ** the property is write-only, or no set method, which means the + ** the property is read-only.)<P> + ** + ** The constraints put on get and set methods are:<P> + ** <OL> + ** <LI>A get method must have signature + ** <CODE><propertyType> <getMethodName>()</CODE></LI> + ** <LI>A set method must have signature + ** <CODE>void <setMethodName>(<propertyType>)</CODE></LI> + ** <LI>Either method type may throw any exception.</LI> + ** <LI>Both methods must be public.</LI> + ** </OL> + ** + ** @author John Keiser + ** @author Robert Schuster (thebohemian@gmx.net) + ** @since 1.1 + ** @status updated to 1.4 + **/ +public class PropertyDescriptor extends FeatureDescriptor +{ + Class<?> propertyType; + Method getMethod; + Method setMethod; + + Class<?> propertyEditorClass; + boolean bound; + boolean constrained; + + PropertyDescriptor(String name) + { + setName(name); + } + + /** Create a new PropertyDescriptor by introspection. + ** This form of constructor creates the PropertyDescriptor by + ** looking for a getter method named <CODE>get<name>()</CODE> + ** (or, optionally, if the property is boolean, + ** <CODE>is<name>()</CODE>) and + ** <CODE>set<name>()</CODE> in class + ** <CODE><beanClass></CODE>, where <name> has its + ** first letter capitalized by the constructor.<P> + ** + ** Note that using this constructor the given property must be read- <strong>and</strong> + ** writeable. If the implementation does not both, a read and a write method, an + ** <code>IntrospectionException</code> is thrown. + ** + ** <B>Implementation note:</B> If there is both are both isXXX and + ** getXXX methods, the former is used in preference to the latter. + ** We do not check that an isXXX method returns a boolean. In both + ** cases, this matches the behaviour of JDK 1.4<P> + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor(String name, Class<?> beanClass) + throws IntrospectionException + { + setName(name); + if (name.length() == 0) + { + throw new IntrospectionException("empty property name"); + } + String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1); + findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps); + + if (getMethod == null) + { + throw new IntrospectionException( + "Cannot find a is" + caps + " or get" + caps + " method"); + } + + if (setMethod == null) + { + throw new IntrospectionException( + "Cannot find a " + caps + " method"); + } + + // finally check the methods compatibility + propertyType = checkMethods(getMethod, setMethod); + } + + /** Create a new PropertyDescriptor by introspection. + ** This form of constructor allows you to specify the + ** names of the get and set methods to search for.<P> + ** + ** <B>Implementation note:</B> If there is a get method (or + ** boolean isXXX() method), then the return type of that method + ** is used to find the set method. If there is no get method, + ** then the set method is searched for exhaustively.<P> + ** + ** <B>Spec note:</B> + ** If there is no get method and multiple set methods with + ** the same name and a single parameter (different type of course), + ** then an IntrospectionException is thrown. While Sun's spec + ** does not state this, it can make Bean behavior different on + ** different systems (since method order is not guaranteed) and as + ** such, can be treated as a bug in the spec. I am not aware of + ** whether Sun's implementation catches this. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @param getMethodName the name of the get method or <code>null</code> if the property is write-only. + ** @param setMethodName the name of the set method or <code>null</code> if the property is read-only. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor( + String name, + Class<?> beanClass, + String getMethodName, + String setMethodName) + throws IntrospectionException + { + setName(name); + findMethods(beanClass, getMethodName, null, setMethodName); + + if (getMethod == null && getMethodName != null) + { + throw new IntrospectionException( + "Cannot find a getter method called " + getMethodName); + } + + if (setMethod == null && setMethodName != null) + { + throw new IntrospectionException( + "Cannot find a setter method called " + setMethodName); + } + + propertyType = checkMethods(getMethod, setMethod); + } + + /** Create a new PropertyDescriptor using explicit Methods. + ** Note that the methods will be checked for conformance to standard + ** Property method rules, as described above at the top of this class. + **<br> + ** It is possible to call this method with both <code>Method</code> arguments + ** being <code>null</code>. In such a case the property type is <code>null</code>. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param readMethod the read method or <code>null</code> if the property is write-only. + ** @param writeMethod the write method or <code>null</code> if the property is read-only. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor( + String name, + Method readMethod, + Method writeMethod) + throws IntrospectionException + { + setName(name); + getMethod = readMethod; + setMethod = writeMethod; + propertyType = checkMethods(getMethod, setMethod); + } + + /** Get the property type. + ** This is the type the get method returns and the set method + ** takes in. + **/ + public Class<?> getPropertyType() + { + return propertyType; + } + + /** Get the get method. Why they call it readMethod here and + ** get everywhere else is beyond me. + **/ + public Method getReadMethod() + { + return getMethod; + } + + /** Sets the read method.<br/> + * The read method is used to retrieve the value of a property. A legal + * read method must have no arguments. Its return type must not be + * <code>void</code>. If this methods succeeds the property type + * is adjusted to the return type of the read method.<br/> + * <br/> + * It is legal to set the read and the write method to <code>null</code> + * or provide method which have been declared in distinct classes. + * + * @param readMethod The new method to be used or <code>null</code>. + * @throws IntrospectionException If the given method is invalid. + * @since 1.2 + */ + public void setReadMethod(Method readMethod) throws IntrospectionException + { + propertyType = checkMethods(readMethod, setMethod); + + getMethod = readMethod; + } + + /** Get the set method. Why they call it writeMethod here and + ** set everywhere else is beyond me. + **/ + public Method getWriteMethod() + { + return setMethod; + } + + /** Sets the write method.<br/> + * The write method is used to set the value of a property. A legal write method + * must have a single argument which can be assigned to the property. If no + * read method exists the property type changes to the argument type of the + * write method.<br/> + * <br/> + * It is legal to set the read and the write method to <code>null</code> + * or provide method which have been declared in distinct classes. + * + * @param writeMethod The new method to be used or <code>null</code>. + * @throws IntrospectionException If the given method is invalid. + * @since 1.2 + */ + public void setWriteMethod(Method writeMethod) + throws IntrospectionException + { + propertyType = checkMethods(getMethod, writeMethod); + + setMethod = writeMethod; + } + + /** Get whether the property is bound. Defaults to false. **/ + public boolean isBound() + { + return bound; + } + + /** Set whether the property is bound. + ** As long as the the bean implements addPropertyChangeListener() and + ** removePropertyChangeListener(), setBound(true) may safely be called.<P> + ** If these things are not true, then the behavior of the system + ** will be undefined.<P> + ** + ** When a property is bound, its set method is required to fire the + ** <CODE>PropertyChangeListener.propertyChange())</CODE> event + ** after the value has changed. + ** @param bound whether the property is bound or not. + **/ + public void setBound(boolean bound) + { + this.bound = bound; + } + + /** Get whether the property is constrained. Defaults to false. **/ + public boolean isConstrained() + { + return constrained; + } + + /** Set whether the property is constrained. + ** If the set method throws <CODE>java.beans.PropertyVetoException</CODE> + ** (or subclass thereof) and the bean implements addVetoableChangeListener() + ** and removeVetoableChangeListener(), then setConstrained(true) may safely + ** be called. Otherwise, the system behavior is undefined. + ** <B>Spec note:</B> given those strict parameters, it would be nice if it + ** got set automatically by detection, but oh well.<P> + ** When a property is constrained, its set method is required to:<P> + ** <OL> + ** <LI>Fire the <CODE>VetoableChangeListener.vetoableChange()</CODE> + ** event notifying others of the change and allowing them a chance to + ** say it is a bad thing.</LI> + ** <LI>If any of the listeners throws a PropertyVetoException, then + ** it must fire another vetoableChange() event notifying the others + ** of a reversion to the old value (though, of course, the change + ** was never made). Then it rethrows the PropertyVetoException and + ** exits.</LI> + ** <LI>If all has gone well to this point, the value may be changed.</LI> + ** </OL> + ** @param constrained whether the property is constrained or not. + **/ + public void setConstrained(boolean constrained) + { + this.constrained = constrained; + } + + /** Get the PropertyEditor class. Defaults to null. **/ + public Class<?> getPropertyEditorClass() + { + return propertyEditorClass; + } + + /** Set the PropertyEditor class. If the class does not implement + ** the PropertyEditor interface, you will likely get an exception + ** late in the game. + ** @param propertyEditorClass the PropertyEditor class for this + ** class to use. + **/ + public void setPropertyEditorClass(Class<?> propertyEditorClass) + { + this.propertyEditorClass = propertyEditorClass; + } + + /** + * Instantiate a property editor using the property editor class. + * If no property editor class has been set, this will return null. + * If the editor class has a public constructor which takes a single + * argument, that will be used and the bean parameter will be passed + * to it. Otherwise, a public no-argument constructor will be used, + * if available. This method will return null if no constructor is + * found or if construction fails for any reason. + * @param bean the argument to the constructor + * @return a new PropertyEditor, or null on error + * @since 1.5 + */ + public PropertyEditor createPropertyEditor(Object bean) + { + if (propertyEditorClass == null) + return null; + Constructor c = findConstructor(propertyEditorClass, + new Class[] { Object.class }); + if (c != null) + return instantiateClass(c, new Object[] { bean }); + c = findConstructor(propertyEditorClass, null); + if (c != null) + return instantiateClass(c, null); + return null; + } + + // Helper method to look up a constructor and return null if it is not + // found. + private Constructor findConstructor(Class k, Class[] argTypes) + { + try + { + return k.getConstructor(argTypes); + } + catch (NoSuchMethodException _) + { + return null; + } + } + + // Helper method to instantiate an object but return null on error. + private PropertyEditor instantiateClass(Constructor c, Object[] args) + { + try + { + return (PropertyEditor) c.newInstance(args); + } + catch (InstantiationException _) + { + return null; + } + catch (InvocationTargetException _) + { + return null; + } + catch (IllegalAccessException _) + { + return null; + } + catch (ClassCastException _) + { + return null; + } + } + + private void findMethods( + Class beanClass, + String getMethodName1, + String getMethodName2, + String setMethodName) + throws IntrospectionException + { + try + { + // Try the first get method name + if (getMethodName1 != null) + { + try + { + getMethod = + beanClass.getMethod(getMethodName1, new Class[0]); + } + catch (NoSuchMethodException e) + {} + } + + // Fall back to the second get method name + if (getMethod == null && getMethodName2 != null) + { + try + { + getMethod = + beanClass.getMethod(getMethodName2, new Class[0]); + } + catch (NoSuchMethodException e) + {} + } + + // Try the set method name + if (setMethodName != null) + { + if (getMethod != null) + { + // If there is a get method, use its return type to help + // select the corresponding set method. + Class propertyType = getMethod.getReturnType(); + if (propertyType == Void.TYPE) + { + String msg = + "The property's read method has return type 'void'"; + throw new IntrospectionException(msg); + } + + Class[] setArgs = new Class[] { propertyType }; + try + { + setMethod = beanClass.getMethod(setMethodName, setArgs); + } + catch (NoSuchMethodException e) + {} + } + else if (getMethodName1 == null && getMethodName2 == null) + { + // If this is a write-only property, choose the first set method + // with the required name, one parameter and return type 'void' + Method[] methods = beanClass.getMethods(); + for (int i = 0; i < methods.length; i++) + { + if (methods[i].getName().equals(setMethodName) + && methods[i].getParameterTypes().length == 1 + && methods[i].getReturnType() == Void.TYPE) + { + setMethod = methods[i]; + break; + } + } + } + } + } + catch (SecurityException e) + { + // FIXME -- shouldn't we just allow SecurityException to propagate? + String msg = + "SecurityException thrown on attempt to access methods."; + throw new IntrospectionException(msg); + } + } + + /** Checks whether the given <code>Method</code> instances are legal read and + * write methods. The following requirements must be met:<br/> + * <ul> + * <li>the read method must not have an argument</li> + * <li>the read method must have a non void return type</li> + * <li>the read method may not exist</li> + * <li>the write method must have a single argument</li> + * <li>the property type and the read method's return type must be assignable from the + * write method's argument type</li> + * <li>the write method may not exist</li> + * </ul> + * While checking the methods a common new property type is calculated. If the method + * succeeds this property type is returned.<br/> + * <br/> + * For compatibility this has to be noted:<br/> + * The two methods are allowed to be defined in two distinct classes and may both be null. + * + * @param readMethod The new read method to check. + * @param writeMethod The new write method to check. + * @return The common property type of the two method. + * @throws IntrospectionException If any of the above requirements are not met. + */ + private Class<?> checkMethods(Method readMethod, Method writeMethod) + throws IntrospectionException + { + Class<?> newPropertyType = propertyType; + + // a valid read method has zero arguments and a non-void return type. + if (readMethod != null) + { + if (readMethod.getParameterTypes().length > 0) + { + throw new IntrospectionException("read method has unexpected parameters"); + } + + newPropertyType = readMethod.getReturnType(); + + if (newPropertyType == Void.TYPE) + { + throw new IntrospectionException("read method return type is void"); + } + } + + // a valid write method has one argument which can be assigned to the property + if (writeMethod != null) + { + if (writeMethod.getParameterTypes().length != 1) + { + String msg = "write method does not have exactly one parameter"; + throw new IntrospectionException(msg); + } + + if (readMethod == null) + { + // changes the property type if there is no read method + newPropertyType = writeMethod.getParameterTypes()[0]; + } + else + { + // checks whether the write method can be assigned to the return type of the read + // method (if this is not the case, the methods are not compatible) + // note: newPropertyType may be null if no methods or method names have been + // delivered in the constructor. + if (newPropertyType != null + && !newPropertyType.isAssignableFrom( + writeMethod.getParameterTypes()[0])) + { + // note: newPropertyType is the same as readMethod.getReturnType() at this point + throw new IntrospectionException("read and write method are not compatible"); + } + + /* note: the check whether both method are defined in related classes makes sense but is not + * done in the JDK. + * I leave this code here in case someone at Sun decides to add that functionality in later versions (rschuster) + if ((!readMethod + .getDeclaringClass() + .isAssignableFrom(writeMethod.getDeclaringClass())) + && (!writeMethod + .getDeclaringClass() + .isAssignableFrom(readMethod.getDeclaringClass()))) + { + String msg = + "set and get methods are not in the same class."; + throw new IntrospectionException(msg); + } + */ + + } + } + + return newPropertyType; + } + + /** + * Return a hash code for this object, conforming to the contract described + * in {@link Object#hashCode()}. + * @return the hash code + * @since 1.5 + */ + public int hashCode() + { + return ((propertyType == null ? 0 : propertyType.hashCode()) + | (propertyEditorClass == null ? 0 : propertyEditorClass.hashCode()) + | (bound ? Boolean.TRUE : Boolean.FALSE).hashCode() + | (constrained ? Boolean.TRUE : Boolean.FALSE).hashCode() + | (getMethod == null ? 0 : getMethod.hashCode()) + | (setMethod == null ? 0 : setMethod.hashCode())); + } + + /** Compares this <code>PropertyDescriptor</code> against the + * given object. + * Two PropertyDescriptors are equals if + * <ul> + * <li>the read methods are equal</li> + * <li>the write methods are equal</li> + * <li>the property types are equals</li> + * <li>the property editor classes are equal</li> + * <li>the flags (constrained and bound) are equal</li> + * </ul> + * @return Whether both objects are equal according to the rules given above. + * @since 1.4 + */ + public boolean equals(Object o) + { + if (o instanceof PropertyDescriptor) + { + PropertyDescriptor that = (PropertyDescriptor) o; + + // compares the property types and checks the case where both are null + boolean samePropertyType = + (propertyType == null) + ? that.propertyType == null + : propertyType.equals(that.propertyType); + + // compares the property editor classes and checks the case where both are null + boolean samePropertyEditorClass = + (propertyEditorClass == null) + ? that.propertyEditorClass == null + : propertyEditorClass.equals(that.propertyEditorClass); + + // compares the flags for equality + boolean sameFlags = + bound == that.bound && constrained == that.constrained; + + // compares the read methods and checks the case where both are null + boolean sameReadMethod = + (getMethod == null) + ? that.getMethod == null + : getMethod.equals(that.getMethod); + + boolean sameWriteMethod = + (setMethod == null) + ? that.setMethod == null + : setMethod.equals(that.setMethod); + + return samePropertyType + && sameFlags + && sameReadMethod + && sameWriteMethod + && samePropertyEditorClass; + } + else + { + return false; + } + + } + +} diff --git a/libjava/classpath/java/beans/PropertyEditor.java b/libjava/classpath/java/beans/PropertyEditor.java new file mode 100644 index 000000000..5fba014ea --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditor.java @@ -0,0 +1,209 @@ +/* java.beans.PropertyEditor + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** PropertyEditors are custom GUI editors for specific types of values. + ** + ** A PropertyEditor can be used, for example, if you are editing a type of value + ** that can be more easily represented graphically, such as a Point, or one that + ** can be more easily represented by a list, such as a boolean (true/false).<P> + ** + ** A PropertyEditor must be able to display its contents when asked to and + ** be able to allow the user to change its underlying field value. However, it + ** is not the PropertyEditor's responsibility to make the change to the + ** underlying Object; in fact, the PropertyEditor does not even know about the + ** Object it is actually editing--only about the property it is currently + ** editing. When a change is made to the property, the PropertyEditor must + ** simply fire a PropertyChangeEvent and allow the RAD tool to actually set + ** the property in the underlying Bean.<P> + ** + ** PropertyEditors should not change the Objects they are given by setValue(). + ** These Objects may or may not be the actual Objects which are properties of + ** the Bean being edited. Instead, PropertyEditors should create a new Object + ** and fire a PropertyChangeEvent with the old and new values.<P> + ** + ** PropertyEditors also must support the ability to return a Java + ** initialization string. See the getJavaInitializationString() method for + ** details.<P> + ** + ** There are several different ways a PropertyEditor may display and control + ** editing of its value. When multiple types of input and display are + ** given by a single PropertyEditor, the RAD tool may decide which of the call + ** to support. Some RAD tools may even be text-only, so even if you support + ** a graphical set and get, it may choose the text set and get whenever it can. + ** <OL> + ** <LI>Every PropertyEditor must support getValue() and setValue(). For + ** setValue(), the component must only support it when the argument is + ** the same type that the PropertyEditor supports.</LI> + ** <LI>Every PropertyEditor must support getJavaInitializationString().</LI> + ** <LI>You may support painting the value yourself if you wish. To do this, + ** have isPaintable() return true and implement the paintValue() method. + ** This method does not determine in any way how the value is edited; + ** merely how it is displayed.</LI> + ** <LI>Let the caller of the PropertyEditor give the user a text input. Do + ** this by returning a non-null String from getAsText(). If you support + ** text input, you *must* support setAsText().</LI> + ** <LI>Give the caller a set of possible values, such as "true"/"false", that + ** the user must select from. To do this, return the list of Strings + ** from the getTags() method. The RAD tool may choose to implement the + ** user input any way it wishes, and only guarantees that setAsText() will + ** only be called with one of the Strings returned from getTags().</LI> + ** <LI>You may support a whole custom editing control by supporting + ** getCustomEditor(). To do this, return true from supportsCustomEditor() + ** and return a Component that does the job. It is the component's job, + ** or the PropertyEditor's job, to make sure that when the editor changes + ** its value, the PropertyChangeEvent is thrown.</LI> + ** </OL> + ** + ** The PropertyEditor for a particular Bean can be found using the + ** PropertyEditorManager class, which goes through a series of different + ** checks to find the appropriate class.<P> + ** + ** A PropertyChangeEvent should be thrown from the PropertyEditor whenever a + ** bound property (a property PropertyDescriptor.isBound() set to true) + ** changes. When this happens, the editor itself should *not* change the value + ** itself, but rather allow the RAD tool to call setValue() or setAsText(). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 30 June 1998 + ** @see java.beans.PropertyEditorManager + ** @see java.beans.PropertyEditorSupport + **/ + +public interface PropertyEditor { + /** Called by the RAD tool to set the value of this property for the PropertyEditor. + ** If the property type is native, it should be wrapped in the appropriate + ** wrapper type. + ** @param value the value to set this property to. + **/ + void setValue(Object value); + + /** Accessor method to get the current value the PropertyEditor is working with. + ** If the property type is native, it will be wrapped in the appropriate + ** wrapper type. + ** @return the current value of the PropertyEditor. + **/ + Object getValue(); + + + /** Set the value of this property using a String. + ** Whether or not this PropertyEditor is editing a String type, this converts + ** the String into the type of the PropertyEditor. + ** @param text the text to set it to. + ** @exception IllegalArgumentException if the String is in the wrong format or setAsText() is not supported. + **/ + void setAsText(String text) throws IllegalArgumentException; + + /** Get the value of this property in String format. + ** Many times this can simply use Object.toString().<P> + ** Return null if you do not support getAsText()/setAsText(). + ** <code>setAsText(getAsText())</code> should be valid; i.e. the stuff you spit out in + ** getAsText() should be able to go into setAsText(). + ** @return the value of this property in String format. + **/ + String getAsText(); + + /** Get a list of possible Strings which this property type can have. + ** The value of these will be used by the RAD tool to construct some sort + ** of list box or to check text box input, and the resulting String passed + ** to setAsText() should be one of these. Note, however, that like most things + ** with this mammoth, unwieldy interface, this is not guaranteed. Thus, you + ** must check the value in setAsText() anyway. + ** @return the list of possible String values for this property type. + **/ + String[] getTags(); + + + /** The RAD tool calls this to find out whether the PropertyEditor can paint itself. + ** @return true if it can paint itself graphically, false if it cannot. + **/ + boolean isPaintable(); + + /** The RAD tool calls this to paint the actual value of the property. + ** The Graphics context will have the same current font, color, etc. as the + ** parent Container. You may safely change the font, color, etc. and not + ** change them back.<P> + ** This method should do a silent no-op if isPaintable() is false. + ** @param g the Graphics context to paint on + ** @param bounds the rectangle you have reserved to work in + **/ + void paintValue(java.awt.Graphics g, java.awt.Rectangle bounds); + + + /** The RAD tool calls this to find out whether the PropertyEditor supports a custom component to edit and display itself. + ** @return true if getCustomEditor() will return a component, false if not. + **/ + boolean supportsCustomEditor(); + + /** The RAD tool calls this to grab the component that can edit this type. + ** The component may be painted anywhere the RAD tool wants to paint it-- + ** even in its own window.<P> + ** The component must hook up with the PropertyEditor and, whenever a + ** change to the value is made, fire a PropertyChangeEvent to the source.<P> + ** @return the custom editor for this property type. + **/ + java.awt.Component getCustomEditor(); + + + /** Adds a property change listener to this PropertyEditor. + ** @param listener the listener to add + **/ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** Removes a property change listener from this PropertyEditor. + ** @param listener the listener to remove + **/ + void removePropertyChangeListener(PropertyChangeListener listener); + + /** Get a Java language-specific String which could be used to create an Object + ** of the specified type. Every PropertyEditor must support this.<P> + ** The reason for this is that while most RAD tools will serialize the Beans + ** and deserialize them at runtime, some RAD tools will generate code that + ** creates the Beans. Examples of Java initialization strings would be:<P> + ** <OL> + ** <LI><CODE>2</CODE></LI> + ** <LI><CODE>"I am a String"</CODE></LI> + ** <LI><CODE>new MyObject(2, "String", new StringBuffer())</CODE></LI> + ** </OL> + ** @return the initialization string for this object in Java. + **/ + String getJavaInitializationString(); +} diff --git a/libjava/classpath/java/beans/PropertyEditorManager.java b/libjava/classpath/java/beans/PropertyEditorManager.java new file mode 100644 index 000000000..253ddafe1 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditorManager.java @@ -0,0 +1,216 @@ +/* java.beans.PropertyEditorManager + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.editors.ColorEditor; +import gnu.java.beans.editors.FontEditor; +import gnu.java.beans.editors.NativeBooleanEditor; +import gnu.java.beans.editors.NativeByteEditor; +import gnu.java.beans.editors.NativeDoubleEditor; +import gnu.java.beans.editors.NativeFloatEditor; +import gnu.java.beans.editors.NativeIntEditor; +import gnu.java.beans.editors.NativeLongEditor; +import gnu.java.beans.editors.NativeShortEditor; +import gnu.java.beans.editors.StringEditor; +import gnu.java.lang.ClassHelper; + +import java.awt.Color; +import java.awt.Font; + +/** + * PropertyEditorManager is used to find property editors + * for various types (not necessarily Beans).<P> + * + * It first checks to see if the property editor is + * already registered; if it is, that property editor is + * used. Next it takes the type's classname and appends + * "Editor" to it, and searches first in the class's + * package and then in the property editor search path. + * + * <p>Default property editors are provided for:</p> + * + * <ol> + * <li>boolean, byte, short, int, long, float, and double</li> + * <li>java.lang.String</li> + * <li>java.awt.Color</li> + * <li>java.awt.Font</li> + * </ol> + * + * <p><strong>Spec Suggestion:</strong> Perhaps an editor for + * Filename or something like it should be provided. As well + * as char.</p> + * + * @author John Keiser + * @since 1.1 + * @version 1.1.0, 29 Jul 1998 + */ + +public class PropertyEditorManager +{ + static java.util.Hashtable<Class<?>,Class<?>> editors = + new java.util.Hashtable<Class<?>,Class<?>>(); + static String[] editorSearchPath = { "gnu.java.beans.editors", + "sun.beans.editors" }; + + static + { + registerEditor(Boolean.TYPE, NativeBooleanEditor.class); + registerEditor(Byte.TYPE, NativeByteEditor.class); + registerEditor(Short.TYPE, NativeShortEditor.class); + registerEditor(Integer.TYPE, NativeIntEditor.class); + registerEditor(Long.TYPE, NativeLongEditor.class); + registerEditor(Float.TYPE, NativeFloatEditor.class); + registerEditor(Double.TYPE, NativeDoubleEditor.class); + registerEditor(String.class, StringEditor.class); + registerEditor(Color.class, ColorEditor.class); + registerEditor(Font.class, FontEditor.class); + } + + /** + * Beats me why this class can be instantiated, but there + * you have it. + */ + public PropertyEditorManager() + { + // Do nothing here + } + + /** + * Register an editor for a class. Replaces old editor + * if there was one registered before. + * + * @param editedClass the class that the property editor + * will edit. + * @param editorClass the PropertyEditor class. + */ + public static void registerEditor(Class<?> editedClass, Class<?> editorClass) + { + editors.put(editedClass, editorClass); + } + + /** + * Returns a new instance of the property editor for the + * specified class. + * + * @param editedClass the class that the property editor + * will edit. + * @return a PropertyEditor instance that can edit the + * specified class. + */ + public static PropertyEditor findEditor(Class<?> editedClass) + { + try + { + Class found = (Class)editors.get(editedClass); + if(found != null) + { + return (PropertyEditor)found.newInstance(); + } + + ClassLoader contextClassLoader + = Thread.currentThread().getContextClassLoader(); + + try + { + found = Class.forName(editedClass.getName()+"Editor", true, + contextClassLoader); + registerEditor(editedClass,found); + return (PropertyEditor)found.newInstance(); + } + catch(ClassNotFoundException E) + { + } + + String appendName + = "." + + ClassHelper.getTruncatedClassName(editedClass) + + "Editor"; + synchronized(editorSearchPath) + { + for(int i=0;i<editorSearchPath.length;i++) + { + try + { + found = Class.forName(editorSearchPath[i] + appendName, + true, contextClassLoader); + registerEditor(editedClass,found); + return (PropertyEditor)found.newInstance(); + } + catch(ClassNotFoundException E) + { + } + } + } + } + catch(InstantiationException E) + { + } + catch(IllegalAccessException E) + { + } + + return null; + } + + /** + * Get the editor search path. + * As a minor departure from the spec, the default value + * for the editor search path is "gnu.java.beans.editors", + * "sun.beans.editors". + * + * @return the editor search path. + */ + public static String[] getEditorSearchPath() + { + return editorSearchPath; + } + + /** + * Set the editor search path. + * + * @param editorSearchPath the new value for the editor search path. + */ + public static void setEditorSearchPath(String[] editorSearchPath) + { + synchronized(editorSearchPath) + { + PropertyEditorManager.editorSearchPath = editorSearchPath; + } + } +} diff --git a/libjava/classpath/java/beans/PropertyEditorSupport.java b/libjava/classpath/java/beans/PropertyEditorSupport.java new file mode 100644 index 000000000..bb68e0e31 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditorSupport.java @@ -0,0 +1,265 @@ +/* java.beans.PropertyEditorSupport + Copyright (C) 1998, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + + +/** + * PropertyEditorSupport helps with PropertyEditors, + * implementing base functionality that they usually must + * have but which is a pain to implement. You may extend + * from this class or use it as a standalone.<P> + * + * This class does not do any painting or actual editing. + * For that, you must use or extend it. See the + * PropertyEditor class for better descriptions of what + * the various methods do. + * + * @author John Keiser + * @author Robert Schuster + * @since 1.1 + * @status updated to 1.5 + */ +public class PropertyEditorSupport implements PropertyEditor +{ + Object eventSource; + Object value; + PropertyChangeSupport pSupport; + + /** Call this constructor when you are deriving from + * PropertyEditorSupport. + * + * Using this constructor the event source is this PropertyEditorSupport + * instance itself. + * + * @since 1.5 + * @specnote this was <code>protected</code> prior to 1.5 + */ + public PropertyEditorSupport() + { + eventSource = this; + pSupport = new PropertyChangeSupport(this); + } + + /** Call this constructor when you are using + * PropertyEditorSupport as a helper object. + * + * This constructor throws a NullPointerException when <code>source</code> is <code>null</code>, + * for compatibility reasons with J2SDK 1.5.0 . + * + * @param source The source to use when firing + * property change events. + * @since 1.5 + * @specnote this was <code>protected</code> prior to 1.5 + */ + public PropertyEditorSupport(Object source) + { + // note: constructor rejects source being null for the sake of compatibility + // with official 1.5.0 implementation + if (source == null) + throw new NullPointerException("Event source must not be null."); + + eventSource = source; + pSupport = new PropertyChangeSupport(eventSource); + } + + /** Sets the current value of the property and a property change + * event is fired to all registered PropertyChangeListener instances. + * + * @param newValue The new value for the property. + */ + public void setValue(Object newValue) + { + value = newValue; + + // specification in java.beans.PropertyChangeEvent says + // that without a property name (first argument) the + // new and the old value should always be null + pSupport.firePropertyChange(null, null, null); + } + + /** Gets the current value of the property. + * + * @return the current value of the property. + */ + public Object getValue() + { + return value; + } + + /** Gets whether this object is paintable or not. + * + * @return <CODE>false</CODE> + */ + public boolean isPaintable() + { + return false; + } + + /** Paints this object. This class does nothing in + * this method. + */ + public void paintValue(java.awt.Graphics g, java.awt.Rectangle r) + { + } + + /** Gets the Java initialization String for the current + * value of the Object. This class returns gibberish or + * null (though the spec does not say which).<P> + * <STRONG>Implementation Note:</STRONG> This class + * returns the string "@$#^" to make sure the code will + * be broken, so that you will know to override it when + * you create your own property editor. + * + * @return the Java initialization string. + */ + public String getJavaInitializationString() + { + return "@$#^"; + } + + /** Gets the value as text. + * In this class, you cannot count on getAsText() doing + * anything useful, although in this implementation I + * do toString(). + * + * @return the value as text. + */ + public String getAsText() + { + return value != null ? value.toString() : "null"; + } + + /** Sets the value as text. + * In this class, you cannot count on setAsText() doing + * anything useful across implementations. + * <STRONG>Implementation Note:</STRONG> In this + * implementation it checks if the String is "null", and + * if it is, sets the value to null, otherwise it throws + * an IllegalArgumentException. + * + * @param s the text to convert to a new value. + * @exception IllegalArgumentException if the text is + * malformed. + */ + public void setAsText(String s) throws IllegalArgumentException + { + if (s.equals("null")) + setValue(null); + else + throw new IllegalArgumentException(); + } + + /** Returns a list of possible choices for the value. + * + * @return <CODE>null</CODE> + */ + public String[] getTags() + { + return null; + } + + /** Returns a custom component to edit the value. + * + * @return <CODE>null</CODE> in this class. + */ + public java.awt.Component getCustomEditor() + { + return null; + } + + /** Finds out whether this property editor supports a + * custom component to edit its value. + * + * @return <CODE>false</CODE> in this class. + */ + public boolean supportsCustomEditor() + { + return false; + } + + /** Adds a property change listener to this property editor. + * + * @param l the listener to add. + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + pSupport.addPropertyChangeListener(l); + } + + /** Removes a property change listener from this property editor. + * + * @param l the listener to remove. + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + pSupport.removePropertyChangeListener(l); + } + + /** Notifies people that we've changed, although we don't + * tell them just how. + */ + public void firePropertyChange() + { + pSupport.firePropertyChange(null, null, null); + } + + /** Returns the bean that is used as the source of events. + * + * @return The event source object + * @since 1.5 + */ + public Object getSource() + { + return eventSource; + } + + /** Sets the bean that is used as the source of events + * when property changes occur. + * + * The event source bean is for informational purposes only + * and should not be changed by the <code>PropertyEditor</code>. + * + * @param source + * @since 1.5 + */ + public void setSource(Object source) + { + eventSource = source; + } +} diff --git a/libjava/classpath/java/beans/PropertyVetoException.java b/libjava/classpath/java/beans/PropertyVetoException.java new file mode 100644 index 000000000..1f0399b4b --- /dev/null +++ b/libjava/classpath/java/beans/PropertyVetoException.java @@ -0,0 +1,85 @@ +/* PropertyVetoException.java -- thrown to veto a proposed property change + Copyright (C) 1998, 2000, 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * PropertyVetoException is thrown when a VetoableChangeListener doesn't + * like the proposed change. + * + * @author John Keiser + * @see VetoableChangeListener + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyVetoException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 129596057694162164L; + + /** + * The vetoed change. + * + * @serial the event that was vetoed + */ + private final PropertyChangeEvent evt; + + /** + * Instantiate this exception with the given message and property change. + * + * @param msg the reason for the veto + * @param changeEvent the PropertyChangeEvent that was thrown + */ + public PropertyVetoException(String msg, PropertyChangeEvent changeEvent) + { + super(msg); + evt = changeEvent; + } + + /** + * Get the PropertyChange event that was vetoed. + * + * @return the vetoed change + */ + public PropertyChangeEvent getPropertyChangeEvent() + { + return evt; + } +} diff --git a/libjava/classpath/java/beans/SimpleBeanInfo.java b/libjava/classpath/java/beans/SimpleBeanInfo.java new file mode 100644 index 000000000..d5216caf5 --- /dev/null +++ b/libjava/classpath/java/beans/SimpleBeanInfo.java @@ -0,0 +1,145 @@ +/* java.beans.SimpleBeanInfo + Copyright (C) 1998, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.awt.Image; +import java.awt.Toolkit; +import java.net.URL; + +/** + ** SimpleBeanInfo is a class you may extend to more easily + ** provide select information to the Introspector. It + ** implements all of the methods in BeanInfo by returning + ** null and forces the Introspector to behave exactly as + ** if there were no BeanInfo class at all (Introspecting + ** everything).<P> + ** + ** Overriding one or two of these functions + ** to give explicit information on only those things you + ** wish to give explicit information is perfectly safe, + ** and even desirable.<P> + ** + ** See the BeanInfo class for information on what the + ** various methods actually do. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 29 Jul 1998 + ** @see java.beans.BeanInfo + **/ + +public class SimpleBeanInfo implements BeanInfo { + /** Force Introspection of the general bean info. + ** @return <CODE>null</CODE>. + **/ + public BeanDescriptor getBeanDescriptor() { + return null; + } + + /** Force Introspection of the events this Bean type + ** fires. + ** @return <CODE>null</CODE> + **/ + public EventSetDescriptor[] getEventSetDescriptors() { + return null; + } + + /** Say that there is no "default" event set. + ** @return <CODE>-1</CODE>. + **/ + public int getDefaultEventIndex() { + return -1; + } + + /** Force Introspection of the Bean properties. + ** @return <CODE>null</CODE>. + **/ + public PropertyDescriptor[] getPropertyDescriptors() { + return null; + } + + /** Say that there is no "default" property. + ** @return <CODE>-1</CODE>. + **/ + public int getDefaultPropertyIndex() { + return -1; + } + + /** Force Introspection of the Bean's methods. + ** @return <CODE>null</CODE>. + **/ + public MethodDescriptor[] getMethodDescriptors() { + return null; + } + + /** Tell the Introspector to go look for other BeanInfo + ** itself. + ** @return <CODE>null</CODE>. + **/ + public BeanInfo[] getAdditionalBeanInfo() { + return null; + } + + /** Say that this Bean has no icons. + ** @param iconType the type of icon + ** @return <CODE>null</CODE>. + **/ + public Image getIcon(int iconType) { + return null; + } + + /** Helper method to load an image using the Bean class + ** getResource() method on the BeanInfo class (using + ** getClass(), since you'll extend this class to get + ** the BeanInfo). Basically it's assumed that the Bean + ** and its BeanInfo are both loaded by the same + ** ClassLoader, generally a reasonable assumption. + ** @param location the URL relative + ** @return the Image in question (possibly <code>null</code>). + **/ + public Image loadImage(String location) + { + if (location == null) + return null; + URL url = getClass().getResource(location); + if (url == null) + return null; + return Toolkit.getDefaultToolkit().getImage(url); + } +} diff --git a/libjava/classpath/java/beans/Statement.java b/libjava/classpath/java/beans/Statement.java new file mode 100644 index 000000000..d9cd304da --- /dev/null +++ b/libjava/classpath/java/beans/Statement.java @@ -0,0 +1,386 @@ +/* Statement.java + Copyright (C) 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.lang.CPStringBuilder; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * <p>A Statement captures the execution of an object method. It stores + * the object, the method to call, and the arguments to the method and + * provides the ability to execute the method on the object, using the + * provided arguments.</p> + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class Statement +{ + private Object target; + private String methodName; + private Object[] arguments; + + /** + * One or the other of these will get a value after execute is + * called once, but not both. + */ + private transient Method method; + private transient Constructor ctor; + + /** + * <p>Constructs a statement representing the invocation of + * object.methodName(arg[0], arg[1], ...);</p> + * + * <p>If the argument array is null it is replaced with an + * array of zero length.</p> + * + * @param target The object to invoke the method on. + * @param methodName The object method to invoke. + * @param arguments An array of arguments to pass to the method. + */ + public Statement(Object target, String methodName, Object[] arguments) + { + this.target = target; + this.methodName = methodName; + this.arguments = (arguments != null) ? arguments : new Object[0]; + } + + /** + * Execute the statement. + * + * <p>Finds the specified method in the target object and calls it with + * the arguments given in the constructor.</p> + * + * <p>The most specific method according to the JLS(15.11) is used when + * there are multiple methods with the same name.</p> + * + * <p>Execute performs some special handling for methods and + * parameters: + * <ul> + * <li>Static methods can be executed by providing the class as a + * target.</li> + * + * <li>The method name new is reserved to call the constructor + * new() will construct an object and return it. Not useful unless + * an expression :-)</li> + * + * <li>If the target is an array, get and set as defined in + * java.util.List are recognized as valid methods and mapped to the + * methods of the same name in java.lang.reflect.Array.</li> + * + * <li>The native datatype wrappers Boolean, Byte, Character, Double, + * Float, Integer, Long, and Short will map to methods that have + * native datatypes as parameters, in the same way as Method.invoke. + * However, these wrappers also select methods that actually take + * the wrapper type as an argument.</li> + * </ul> + * </p> + * + * <p>The Sun spec doesn't deal with overloading between int and + * Integer carefully. If there are two methods, one that takes an + * Integer and the other taking an int, the method chosen is not + * specified, and can depend on the order in which the methods are + * declared in the source file.</p> + * + * @throws Exception if an exception occurs while locating or + * invoking the method. + */ + public void execute() throws Exception + { + doExecute(); + } + + private static Class wrappers[] = + { + Boolean.class, Byte.class, Character.class, Double.class, Float.class, + Integer.class, Long.class, Short.class + }; + + private static Class natives[] = + { + Boolean.TYPE, Byte.TYPE, Character.TYPE, Double.TYPE, Float.TYPE, + Integer.TYPE, Long.TYPE, Short.TYPE + }; + + /** Given a wrapper class, return the native class for it. + * <p>For example, if <code>c</code> is <code>Integer</code>, + * <code>Integer.TYPE</code> is returned.</p> + */ + private Class unwrap(Class c) + { + for (int i = 0; i < wrappers.length; i++) + if (c == wrappers[i]) + return natives[i]; + return null; + } + + /** Returns <code>true</code> if all args can be assigned to + * <code>params</code>, <code>false</code> otherwise. + * + * <p>Arrays are guaranteed to be the same length.</p> + */ + private boolean compatible(Class[] params, Class[] args) + { + for (int i = 0; i < params.length; i++) + { + // Argument types are derived from argument values. If one of them was + // null then we cannot deduce its type. However null can be assigned to + // any type. + if (args[i] == null) + continue; + + // Treat Integer like int if appropriate + Class nativeType = unwrap(args[i]); + if (nativeType != null && params[i].isPrimitive() + && params[i].isAssignableFrom(nativeType)) + continue; + if (params[i].isAssignableFrom(args[i])) + continue; + + return false; + } + return true; + } + + /** + * Returns <code>true</code> if the method arguments in first are + * more specific than the method arguments in second, i.e. all + * arguments in <code>first</code> can be assigned to those in + * <code>second</code>. + * + * <p>A method is more specific if all parameters can also be fed to + * the less specific method, because, e.g. the less specific method + * accepts a base class of the equivalent argument for the more + * specific one.</p> + * + * @param first a <code>Class[]</code> value + * @param second a <code>Class[]</code> value + * @return a <code>boolean</code> value + */ + private boolean moreSpecific(Class[] first, Class[] second) + { + for (int j=0; j < first.length; j++) + { + if (second[j].isAssignableFrom(first[j])) + continue; + return false; + } + return true; + } + + final Object doExecute() throws Exception + { + Class klazz = (target instanceof Class) + ? (Class) target : target.getClass(); + Object args[] = (arguments == null) ? new Object[0] : arguments; + Class argTypes[] = new Class[args.length]; + + // Retrieve type or use null if the argument is null. The null argument + // type is later used in compatible(). + for (int i = 0; i < args.length; i++) + argTypes[i] = (args[i] != null) ? args[i].getClass() : null; + + if (target.getClass().isArray()) + { + // FIXME: invoke may have to be used. For now, cast to Number + // and hope for the best. If caller didn't behave, we go boom + // and throw the exception. + if (methodName.equals("get") && argTypes.length == 1) + return Array.get(target, ((Number)args[0]).intValue()); + if (methodName.equals("set") && argTypes.length == 2) + { + Object obj = Array.get(target, ((Number)args[0]).intValue()); + Array.set(target, ((Number)args[0]).intValue(), args[1]); + return obj; + } + throw new NoSuchMethodException("No matching method for statement " + toString()); + } + + // If we already cached the method, just use it. + if (method != null) + return method.invoke(target, args); + else if (ctor != null) + return ctor.newInstance(args); + + // Find a matching method to call. JDK seems to go through all + // this to find the method to call. + + // if method name or length don't match, skip + // Need to go through each arg + // If arg is wrapper - check if method arg is matchable builtin + // or same type or super + // - check that method arg is same or super + + if (methodName.equals("new") && target instanceof Class) + { + Constructor ctors[] = klazz.getConstructors(); + for (int i = 0; i < ctors.length; i++) + { + // Skip methods with wrong number of args. + Class ptypes[] = ctors[i].getParameterTypes(); + + if (ptypes.length != args.length) + continue; + + // Check if method matches + if (!compatible(ptypes, argTypes)) + continue; + + // Use method[i] if it is more specific. + // FIXME: should this check both directions and throw if + // neither is more specific? + if (ctor == null) + { + ctor = ctors[i]; + continue; + } + Class mptypes[] = ctor.getParameterTypes(); + if (moreSpecific(ptypes, mptypes)) + ctor = ctors[i]; + } + if (ctor == null) + throw new InstantiationException("No matching constructor for statement " + toString()); + return ctor.newInstance(args); + } + + Method methods[] = klazz.getMethods(); + + for (int i = 0; i < methods.length; i++) + { + // Skip methods with wrong name or number of args. + if (!methods[i].getName().equals(methodName)) + continue; + Class ptypes[] = methods[i].getParameterTypes(); + if (ptypes.length != args.length) + continue; + + // Check if method matches + if (!compatible(ptypes, argTypes)) + continue; + + // Use method[i] if it is more specific. + // FIXME: should this check both directions and throw if + // neither is more specific? + if (method == null) + { + method = methods[i]; + continue; + } + Class mptypes[] = method.getParameterTypes(); + if (moreSpecific(ptypes, mptypes)) + method = methods[i]; + } + if (method == null) + throw new NoSuchMethodException("No matching method for statement " + toString()); + + // If we were calling Class.forName(String) we intercept and call the + // forName-variant that allows a ClassLoader argument. We take the + // system classloader (aka application classloader) here to make sure + // that application defined classes can be resolved. If we would not + // do that the Class.forName implementation would use the class loader + // of java.beans.Statement which is <null> and cannot resolve application + // defined classes. + if (method.equals( + Class.class.getMethod("forName", new Class[] { String.class }))) + return Class.forName( + (String) args[0], true, ClassLoader.getSystemClassLoader()); + + try { + return method.invoke(target, args); + } catch(IllegalArgumentException iae){ + System.err.println("method: " + method); + + for(int i=0;i<args.length;i++){ + System.err.println("args[" + i + "]: " + args[i]); + } + throw iae; + } + } + + + + /** Return the statement arguments. */ + public Object[] getArguments() { return arguments; } + + /** Return the statement method name. */ + public String getMethodName() { return methodName; } + + /** Return the statement object. */ + public Object getTarget() { return target; } + + /** + * Returns a string representation of this <code>Statement</code>. + * + * @return A string representation of this <code>Statement</code>. + */ + public String toString() + { + CPStringBuilder result = new CPStringBuilder(); + + String targetName; + if (target != null) + targetName = target.getClass().getSimpleName(); + else + targetName = "null"; + + result.append(targetName); + result.append("."); + result.append(methodName); + result.append("("); + + String sep = ""; + for (int i = 0; i < arguments.length; i++) + { + result.append(sep); + result.append( + ( arguments[i] == null ) ? "null" : + ( arguments[i] instanceof String ) ? "\"" + arguments[i] + "\"" : + arguments[i].getClass().getSimpleName()); + sep = ", "; + } + result.append(");"); + + return result.toString(); + } + +} diff --git a/libjava/classpath/java/beans/TODO b/libjava/classpath/java/beans/TODO new file mode 100644 index 000000000..08e1d25ee --- /dev/null +++ b/libjava/classpath/java/beans/TODO @@ -0,0 +1,4 @@ +- add AppletStub and AppletContext to java.beans.Beans.instantiate(). +- make Introspector more efficient. +- basic Introspection tests are in, but more tests are probably in order. +- 1.2 support (waiting on java.lang.Package, mainly) diff --git a/libjava/classpath/java/beans/VetoableChangeListener.java b/libjava/classpath/java/beans/VetoableChangeListener.java new file mode 100644 index 000000000..5107954b0 --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeListener.java @@ -0,0 +1,73 @@ +/* VetoableChangeListener.java -- listen for a change which can be vetoed + Copyright (C) 1998, 2000, 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListener; + +/** + * VetoableChangeListener allows a class to monitor proposed changes to + * properties of a Bean and, if desired, prevent them from occurring. A + * vetoableChange() event will be fired <em>after</em> the property change has + * been requested, but before it is permanent. If any listener rejects the + * change by throwing the PropertyChangeException, a new vetoableChange() + * event will be fired to all listeners who received a vetoableChange() event + * in the first place, informing them to revert back to the old value. Thus, + * the listener that threw the exception the first time should be prepared + * to rethrow it the second time. The value, of course, never actually changed. + * + * <p><strong>Note:</strong> This class may not be reliably used to determine + * whether a property has actually changed. Use the PropertyChangeListener + * interface for that instead. + * + * @author John Keiser + * @see java.beans.PropertyChangeListener + * @see java.beans.VetoableChangeSupport + * @since 1.1 + * @status updated to 1.4 + */ +public interface VetoableChangeListener extends EventListener +{ + /** + * Fired before a Bean's property changes. + * + * @param e the change (containing the old and new values) + * @throws PropertyVetoException if the change is vetoed by the listener + */ + void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException; +} // interface VetoableChangeListener diff --git a/libjava/classpath/java/beans/VetoableChangeListenerProxy.java b/libjava/classpath/java/beans/VetoableChangeListenerProxy.java new file mode 100644 index 000000000..56ca5a38c --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeListenerProxy.java @@ -0,0 +1,102 @@ +/* VetoableChangeListenerProxy.java -- adds a name to a vetoable listener + Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * This class provides an extension to <code>VetoableChangeListener</code> - + * associating a name with the listener. This can be used to filter the + * changes that one is interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status udpated to 1.4 + */ +public class VetoableChangeListenerProxy extends EventListenerProxy + implements VetoableChangeListener +{ + /** + * The name of the property to listen for. Package visible for use by + * VetoableChangeSupport. + */ + final String propertyName; + + /** + * Create a new proxy which filters property change events and only passes + * changes to the named property on to the delegate. + * + * @param propertyName the property's name to filter on + * @param listener the delegate listener + */ + public VetoableChangeListenerProxy(String propertyName, + VetoableChangeListener listener) + { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the event on to the delegate if the property name matches. + * + * @param event the event to pass on, if it meets the filter + * @throws NullPointerException if the delegate this was created with is null + * @throws PropertyVetoException if the change is vetoed by the listener + */ + public void vetoableChange(PropertyChangeEvent event) + throws PropertyVetoException + { + // Note: Sun does not filter, under the assumption that since + // VetoableChangeSupport unwraps proxys, this method should never be + // called by normal use of listeners. + String name = event == null ? null : event.getPropertyName(); + if (name == null ? propertyName == null : name.equals(propertyName)) + ((VetoableChangeListener) getListener()).vetoableChange(event); + } + + /** + * Gets the name of the property this proxy is filtering on. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } +} // class VetoableChangeListenerProxy diff --git a/libjava/classpath/java/beans/VetoableChangeSupport.java b/libjava/classpath/java/beans/VetoableChangeSupport.java new file mode 100644 index 000000000..698e82d8f --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeSupport.java @@ -0,0 +1,532 @@ +/* VetoableChangeSupport.java -- support to manage vetoable change listeners + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006, + 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Vector; + +/** + * VetoableChangeSupport makes it easy to fire vetoable change events and + * handle listeners. It allows chaining of listeners, as well as filtering + * by property name. In addition, it will serialize only those listeners + * which are serializable, ignoring the others without problem. This class + * is thread-safe. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class VetoableChangeSupport implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5090210921595982017L; + + /** + * Maps property names (String) to named listeners (VetoableChangeSupport). + * If this is a child instance, this field will be null. + * + * @serial the map of property names to named listener managers + * @since 1.2 + */ + private Hashtable children; + + /** + * The non-null source object for any generated events. + * + * @serial the event source + */ + private final Object source; + + /** + * A field to compare serialization versions - this class uses version 2. + * + * @serial the serialization format + */ + private static final int vetoableChangeSupportSerializedDataVersion = 2; + + /** + * The list of all registered vetoable listeners. If this instance was + * created by user code, this only holds the global listeners (ie. not tied + * to a name), and may be null. If it was created by this class, as a + * helper for named properties, then this vector will be non-null, and this + * instance appears as a value in the <code>children</code> hashtable of + * another instance, so that the listeners are tied to the key of that + * hashtable entry. + */ + private transient Vector listeners; + + /** + * Create a VetoableChangeSupport to work with a specific source bean. + * + * @param source the source bean to use + * @throws NullPointerException if source is null + */ + public VetoableChangeSupport(Object source) + { + this.source = source; + if (source == null) + throw new NullPointerException(); + } + + /** + * Adds a VetoableChangeListener to the list of global listeners. All + * vetoable change events will be sent to this listener. The listener add + * is not unique: that is, <em>n</em> adds with the same listener will + * result in <em>n</em> events being sent to that listener for every + * vetoable change. This method will unwrap a VetoableChangeListenerProxy, + * registering the underlying delegate to the named property list. + * + * @param l the listener to add (<code>null</code> ignored). + */ + public synchronized void addVetoableChangeListener(VetoableChangeListener l) + { + if (l == null) + return; + if (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + addVetoableChangeListener(p.propertyName, + (VetoableChangeListener) p.getListener()); + } + else + { + if (listeners == null) + listeners = new Vector(); + listeners.add(l); + } + } + + /** + * Removes a VetoableChangeListener from the list of global listeners. If + * any specific properties are being listened on, they must be deregistered + * by themselves; this will only remove the general listener to all + * properties. If <code>add()</code> has been called multiple times for a + * particular listener, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * VetoableChangeListenerProxy, removing the underlying delegate from the + * named property list. + * + * @param l the listener to remove + */ + public synchronized void + removeVetoableChangeListener(VetoableChangeListener l) + { + if (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + removeVetoableChangeListener(p.propertyName, + (VetoableChangeListener) p.getListener()); + } + else if (listeners != null) + { + listeners.remove(l); + if (listeners.isEmpty()) + listeners = null; + } + } + + /** + * Returns an array of all registered vetoable change listeners. Those that + * were registered under a name will be wrapped in a + * <code>VetoableChangeListenerProxy</code>, so you must check whether the + * listener is an instance of the proxy class in order to see what name the + * real listener is registered under. If there are no registered listeners, + * this returns an empty array. + * + * @return the array of registered listeners + * @see VetoableChangeListenerProxy + * @since 1.4 + */ + public synchronized VetoableChangeListener[] getVetoableChangeListeners() + { + ArrayList list = new ArrayList(); + if (listeners != null) + list.addAll(listeners); + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + Vector v = ((VetoableChangeSupport) e.getValue()).listeners; + int j = v.size(); + while (--j >= 0) + list.add(new VetoableChangeListenerProxy + (name, (VetoableChangeListener) v.get(j))); + } + } + return (VetoableChangeListener[]) + list.toArray(new VetoableChangeListener[list.size()]); + } + + /** + * Adds a VetoableChangeListener listening on the specified property. Events + * will be sent to the listener only if the property name matches. The + * listener add is not unique; that is, <em>n</em> adds on a particular + * property for a particular listener will result in <em>n</em> events + * being sent to that listener when that property is changed. The effect is + * cumulative, too; if you are registered to listen to receive events on + * all vetoable changes, and then you register on a particular property, + * you will receive change events for that property twice. This method + * will unwrap a VetoableChangeListenerProxy, registering the underlying + * delegate to the named property list if the names match, and discarding + * it otherwise. + * + * @param propertyName the name of the property to listen on + * @param l the listener to add + */ + public synchronized void addVetoableChangeListener(String propertyName, + VetoableChangeListener l) + { + if (propertyName == null || l == null) + return; + while (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (VetoableChangeListener) p.getListener(); + } + VetoableChangeSupport s = null; + if (children == null) + children = new Hashtable(); + else + s = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + { + s = new VetoableChangeSupport(source); + s.listeners = new Vector(); + children.put(propertyName, s); + } + s.listeners.add(l); + } + + /** + * Removes a VetoableChangeListener from listening to a specific property. + * If <code>add()</code> has been called multiple times for a particular + * listener on a property, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * VetoableChangeListenerProxy, removing the underlying delegate from the + * named property list if the names match. + * + * @param propertyName the property to stop listening on + * @param l the listener to remove + * @throws NullPointerException if propertyName is null + */ + public synchronized void + removeVetoableChangeListener(String propertyName, VetoableChangeListener l) + { + if (children == null) + return; + VetoableChangeSupport s + = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + return; + while (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (VetoableChangeListener) p.getListener(); + } + s.listeners.remove(l); + if (s.listeners.isEmpty()) + { + children.remove(propertyName); + if (children.isEmpty()) + children = null; + } + } + + /** + * Returns an array of all vetoable change listeners registered under the + * given property name. If there are no registered listeners, this returns + * an empty array. + * + * @return the array of registered listeners + * @throws NullPointerException if propertyName is null + * @since 1.4 + */ + public synchronized VetoableChangeListener[] + getVetoableChangeListeners(String propertyName) + { + if (children == null) + return new VetoableChangeListener[0]; + VetoableChangeSupport s + = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + return new VetoableChangeListener[0]; + return (VetoableChangeListener[]) + s.listeners.toArray(new VetoableChangeListener[s.listeners.size()]); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are non-null + * and equal. If the change is vetoed, a new event is fired to notify + * listeners about the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, + Object oldVal, Object newVal) + throws PropertyVetoException + { + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + oldVal, newVal)); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * If the change is vetoed, a new event is fired to notify listeners about + * the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, int oldVal, int newVal) + throws PropertyVetoException + { + if (oldVal != newVal) + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + Integer.valueOf(oldVal), + Integer.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * If the change is vetoed, a new event is fired to notify listeners about + * the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, + boolean oldVal, boolean newVal) + throws PropertyVetoException + { + if (oldVal != newVal) + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + Boolean.valueOf(oldVal), + Boolean.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent to all the global listeners, and to all the + * listeners for the specified property name. This does nothing if old and + * new values of the event are equal. If the change is vetoed, a new event + * is fired to notify listeners about the rollback before the exception is + * thrown. + * + * @param event the event to fire + * @throws NullPointerException if event is null + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(PropertyChangeEvent event) + throws PropertyVetoException + { + if (event.oldValue != null && event.oldValue.equals(event.newValue)) + return; + Vector v = listeners; // Be thread-safe. + if (v != null) + { + int i = v.size(); + try + { + while (--i >= 0) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + } + catch (PropertyVetoException e) + { + event = event.rollback(); + int limit = i; + i = v.size(); + while (--i >= limit) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + throw e; + } + } + Hashtable h = children; // Be thread-safe. + if (h != null && event.propertyName != null) + { + VetoableChangeSupport s + = (VetoableChangeSupport) h.get(event.propertyName); + if (s != null) + { + Vector v1 = s.listeners; // Be thread-safe. + int i = v1 == null ? 0 : v1.size(); + try + { + while (--i >= 0) + ((VetoableChangeListener) v1.get(i)).vetoableChange(event); + } + catch (PropertyVetoException e) + { + event = event.rollback(); + int limit = i; + i = v.size(); + while (--i >= 0) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + i = v1.size(); + while (--i >= limit) + ((VetoableChangeListener) v1.get(i)).vetoableChange(event); + throw e; + } + } + } + } + + /** + * Tell whether the specified property is being listened on or not. This + * will only return <code>true</code> if there are listeners on all + * properties or if there is a listener specifically on this property. + * + * @param propertyName the property that may be listened on + * @return whether the property is being listened on + * @throws NullPointerException if propertyName is null + */ + public synchronized boolean hasListeners(String propertyName) + { + return listeners != null || (children != null + && children.get(propertyName) != null); + } + + /** + * Saves the state of the object to the stream. + * + * @param s the stream to write to + * @throws IOException if anything goes wrong + * @serialData this writes out a null-terminated list of serializable + * global vetoable change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + if (listeners != null) + { + int i = listeners.size(); + while (--i >= 0) + if (listeners.get(i) instanceof Serializable) + s.writeObject(listeners.get(i)); + } + s.writeObject(null); + } + + /** + * Reads the object back from stream (deserialization). + * + * XXX Since serialization for 1.1 streams was not documented, this may + * not work if vetoableChangeSupportSerializedDataVersion is 1. + * + * @param s the stream to read from + * @throws IOException if reading the stream fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads in a null-terminated list of serializable + * global vetoable change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + VetoableChangeListener l = (VetoableChangeListener) s.readObject(); + while (l != null) + { + addVetoableChangeListener(l); + l = (VetoableChangeListener) s.readObject(); + } + // Sun is not as careful with children as we are, and lets some proxys + // in that can never receive events. So, we clean up anything that got + // serialized, to make sure our invariants hold. + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + VetoableChangeSupport vcs = (VetoableChangeSupport) e.getValue(); + if (vcs.listeners == null) + vcs.listeners = new Vector(); + if (vcs.children != null) + vcs.listeners.addAll + (Arrays.asList(vcs.getVetoableChangeListeners(name))); + if (vcs.listeners.size() == 0) + iter.remove(); + else + vcs.children = null; + } + if (children.size() == 0) + children = null; + } + } +} // class VetoableChangeSupport diff --git a/libjava/classpath/java/beans/Visibility.java b/libjava/classpath/java/beans/Visibility.java new file mode 100644 index 000000000..338060181 --- /dev/null +++ b/libjava/classpath/java/beans/Visibility.java @@ -0,0 +1,85 @@ +/* java.beans.Visibility + Copyright (C) 1998, 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * Visibility is an interface a Bean may implement so that the environment + * can tell the Bean whether there is a GUI or not, and so that the Bean + * can tell the environment whether it needs one or can run without one. + * <P> + * + * Sun decided not to use standard Introspection patterns so that these + * methods did not get included when the Introspector made its sweep on + * the class. + * + * @author John Keiser + * @since JDK1.1 + * @version 1.1.0, 29 Jul 1998 + */ + +public interface Visibility { + /** + * Tells whether the Bean can run without a GUI or not. + * @return false if Bean can run without a GUI, else true. + */ + boolean needsGui(); + + /** + * Tells whether Bean is trying not to use the GUI. + * If needsGui() is true, this method should always return false. + * @return true if definitely not using GUI, otherwise false. + */ + boolean avoidingGui(); + + /** + * Tells the Bean not to use GUI methods. + * If needsGUI() is false, then after this method is called, + * avoidingGui() should return true. + */ + void dontUseGui(); + + /** + * Tells the Bean it may use the GUI. + * The Bean is not required to use the GUI in this case, it is + * merely being <EM>permitted</EM> to use it. If needsGui() is + * false, avoidingGui() may return true or false after this method + * is called. + */ + void okToUseGui(); +} diff --git a/libjava/classpath/java/beans/XMLDecoder.java b/libjava/classpath/java/beans/XMLDecoder.java new file mode 100644 index 000000000..26896393a --- /dev/null +++ b/libjava/classpath/java/beans/XMLDecoder.java @@ -0,0 +1,307 @@ +/* java.beans.XMLDecoder -- + Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.DefaultExceptionListener; +import gnu.java.beans.decoder.PersistenceParser; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * The XMLDecoder reads XML data that is structured according to + * <a href="http://java.sun.com/products/jfc/tsc/articles/persistence3/javabeans.dtd">this</a> DTD + * and creates objects according to the content. Usually such data is generated using the + * {@link XMLEncoder} class. + * <p> + * An example XML document might look like this: + * <code> + * <java> + * <string>Hello World</string> + * <int>200</int> + * </java> + * </code> + * <p>To read the <code>String</code> and the <code>Integer</code> instance the following can be used (assume + * the XML data can be obtained from the InputStream):</p> + * <code> + * XMLDecoder decoder = new XMLDecoder(inputStreamContainingXMLData); + * String message = (String) decoder.readObject(); + * Integer number = (Integer) decoder.readObject(); + * </code> + * <p>Besides this basic functionality the <code>XMLDecoder</code> has some more features that might come + * handy in certain situations:</p> + * <p>An owner object can be set using the <code>setOwner</code> method which can then be accessed when + * decoding. This feature is only useful if the XML data is aware of the owner object. Such data may + * look like this (assume that the owner object is a JFrame instance):</p> + * <code> + * <java> + * <void method="getOwner"> + * <void method="setVisible"> + * <boolean>true<boolean> + * </void> + * </void> + * </java> + * </code> + * This accesses the <code>JFrame</code> and makes it visible using the <code>setVisible</code> method. + * <p>Please note that changing the owner <b>after</b> the having read the first object has no effect, + * because all object have been decoded then.</p> + * <p>If the <code>XMLDecoder</code> is created with no {@link ExceptionListener} instance a default one + * is used that prints an error message to <code>System.err</code> whenever a recoverable exception + * is thrown. Recovarable exceptions occur when the XML data cannot be interpreted correctly (e.g + * unknown classes or methods, invocation on null, ...). In general be very careful when the + * <code>XMLDecoder</code> provoked such exceptions because the resulting object(s) may be in an + * undesirable state.</p> + * <p>Note that changing the ExceptionListener instance after <code>readObject</code> has been called + * once has no effect because the decoding is completed then.</p> + * <p>At last one can provide a specific <code>ClassLoader</code> which is then used when <code>Class</code> + * objects are accessed. See {@link java.lang.Class#forName(String, boolean, ClassLoader)} for details + * on this.</p> + * <p>Note: If the <code>InputStream</code> instance given to any of the constructors is <code>null</code> + * the resulting <code>XMLDecoder</code> will be silently (without any exception) useless. Each call + * to <code>readObject</code> will return <code>null</code> and never throws an + * <code>ArrayIndexOutOfBoundsException</code>.</p> + * + * @author Robert Schuster + * @since 1.4 + * @status updated to 1.5 + */ +public class XMLDecoder +{ + private Object owner; + + private ExceptionListener exceptionListener; + + private InputStream inputStream; + + private boolean isStreamClosed; + + private ClassLoader classLoader; + + private Iterator iterator; + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * Using this constructor no special ClassLoader, a default ExceptionListener + * and no owner object is used. + * + * @param in InputStream to read XML data from. + */ + public XMLDecoder(InputStream in) + { + this(in, null); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * Using this constructor no special ClassLoader and a default ExceptionListener + * is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + */ + public XMLDecoder(InputStream in, Object owner) + { + this(in, owner, null); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * If the ExceptionListener argument is null a default implementation is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + * @param exceptionListener ExceptionListener instance to which exception notifications are send. + */ + public XMLDecoder( + InputStream in, + Object owner, + ExceptionListener exceptionListener) + { + this( + in, + owner, + exceptionListener, + Thread.currentThread().getContextClassLoader()); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * If the ExceptionListener argument is null a default implementation is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + * @param listener ExceptionListener instance to which exception notifications are send. + * @param cl ClassLoader instance that is used for calls to <code>Class.forName(String, boolean, ClassLoader)</code> + * @since 1.5 + */ + public XMLDecoder( + InputStream in, + Object owner, + ExceptionListener listener, + ClassLoader cl) + { + // initially here was a check for the validity of the InputStream argument but some + // great engineers decided that this API should silently discard this and behave rather + // odd: readObject will always return null ... + inputStream = in; + + setExceptionListener(listener); + + // validity of this object is checked in Class.forName() and therefore may be null + classLoader = cl; + + this.owner = owner; + } + + /** Closes the stream associated with this decoder. This should be done after having read all + * decoded objects. + * <p>See the description of the {@link #readObject()} for the effect caused by <code>close</code>.</p> + */ + public void close() + { + if (isStreamClosed) + { + return; + } + + try + { + inputStream.close(); + isStreamClosed = true; + } + catch (IOException e) + { + // bad style forced by original API design ... + } + } + + /** Returns the ExceptionListener instance associated with this decoder. + * <p>See the description of {@link XMLDecoder} class for more information on the ExceptionListener.</p> + * + * @return Current ExceptionListener of the decoder. + */ + public ExceptionListener getExceptionListener() + { + return exceptionListener; + } + + /** Returns the owner object of the decoder. This method is usually called + * from within the parsed XML data. + * <p>See the description of {@link XMLDecoder} class for more information on the owner object.</p> + * + * @return The owner object of this decoder. + */ + public Object getOwner() + { + return owner; + } + + /** Returns the next available decoded object. + * <p>Note that the actual decoding takes place when the method is called for the first time.</p> + * <p>If the <code>close</code> method was already called a <code>NoSuchElementException</code> + * is thrown.</p> + * <p>If the InputStream instance used in the constructors was <code>null</code> this method + * will always return <code>null</code> itself.</p> + * + * @return The next object in a sequence decoded from XML data. + * @throws ArrayIndexOutOfBoundsException When no more objects are available. + */ + public Object readObject() throws ArrayIndexOutOfBoundsException + { + // note: the RI does it this way ... + if(inputStream == null) { + return null; + } + + // note: the original API documentation says nothing on what to do + // when the stream was closed before readObject is called but it actually + // throws a NoSuchElementException - this behaviour is imitated here + if (isStreamClosed) + { + throw new NoSuchElementException("Cannot read any objects - XMLDecoder was already closed."); + } + + // creates the PersistenceParser (doing the parsing and decoding) and returns its + // Iterator on first invocation + if (iterator == null) + { + iterator = + new PersistenceParser( + inputStream, + exceptionListener, + classLoader, + this) + .iterator(); + } + + // note: done according to the official documentation + if (!iterator.hasNext()) + { + throw new ArrayIndexOutOfBoundsException("No more objects available from this XMLDecoder."); + } + + // returns just the next object if there was no problem + return iterator.next(); + } + + /** Sets the ExceptionListener instance to which notifications of exceptions are send + * while parsing the XML data. + * <p>See the description of {@link XMLDecoder} class for more information on the ExceptionListener.</p> + * + * @param listener + */ + public void setExceptionListener(ExceptionListener listener) + { + // uses a default implementation when null + if (listener == null) + { + listener = DefaultExceptionListener.INSTANCE; + } + exceptionListener = listener; + } + + /** Sets the owner object which can be accessed from the parsed XML data. + * <p>See the description of {@link XMLDecoder} class for more information on the owner object.</p> + * + * @param newOwner + */ + public void setOwner(Object newOwner) + { + owner = newOwner; + } + +} diff --git a/libjava/classpath/java/beans/XMLEncoder.java b/libjava/classpath/java/beans/XMLEncoder.java new file mode 100644 index 000000000..40cb6dbfb --- /dev/null +++ b/libjava/classpath/java/beans/XMLEncoder.java @@ -0,0 +1,267 @@ +/* XMLEncoder.java + Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.encoder.ScanEngine; + +import java.io.OutputStream; + +/** + * This class uses the {@link PersistenceDelegate} and {@link Encoder} + * infrastructure to generate an XML representation of the objects it + * serializes. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class XMLEncoder extends Encoder +{ + Object owner; + + Exception exception; + + ScanEngine scanEngine; + + private int accessCounter = 0; + + public XMLEncoder(OutputStream os) + { + scanEngine = new ScanEngine(os); + } + + public void close() + { + if (scanEngine != null) + { + scanEngine.close(); + scanEngine = null; + } + } + + public void flush() + { + scanEngine.flush(); + } + + public void writeExpression(Expression expr) + { + // Implementation note: Why is this method overwritten and nearly exactly + // reimplemented as in Encoder? + // The Encoder class can (and should be) subclassed by users outside of the + // java.beans package. While I have doubts that this is possible from an + // API design point of view I tried to replicate the Encoder's behavior + // in the JDK as exactly as possible. This strictness however made it + // extremely complicated to implement the XMLEncoder's backend. Therefore + // I decided to copy the Encoder's implementation and make all changes + // I needed for a succesfull operation of XMLEncoder. + // + // The same is true for the writeStatement method. + + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + scanEngine.writeExpression(expr); + + + Object target = expr.getTarget(); + Object value = null; + Object newValue = null; + + try + { + value = expr.getValue(); + } + catch (Exception e) + { + getExceptionListener().exceptionThrown(e); + return; + } + + + newValue = get(value); + + if (newValue == null) + { + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + + // May happen if exception was thrown. + if (newTarget == null) + { + return; + } + } + + Object[] args = expr.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + } + + Expression newExpr = new Expression(newTarget, expr.getMethodName(), + newArgs); + + // Fakes the result of Class.forName(<primitiveType>) to make it possible + // to hand such a type to the encoding process. + if (value instanceof Class && ((Class) value).isPrimitive()) + newExpr.setValue(value); + + // Instantiates the new object. + try + { + newValue = newExpr.getValue(); + + putCandidate(value, newValue); + } + catch (Exception e) + { + getExceptionListener().exceptionThrown(e); + + // In Statement.writeExpression we had no possibility to flags + // an erroneous state to the ScanEngine without behaving different + // to the JDK. + scanEngine.revoke(); + + return; + } + + writeObject(value); + + } + else if(value.getClass() == String.class || value.getClass() == Class.class) + { + writeObject(value); + } + + scanEngine.end(); + } + + public void writeStatement(Statement stmt) + { + // In case of questions have a at the implementation note in + // writeExpression. + + scanEngine.writeStatement(stmt); + + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + Object target = stmt.getTarget(); + + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + } + + Object[] args = stmt.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + // Here is the difference to the original writeStatement + // method in Encoder. In case that the object is known or + // not an immutable we put it directly into the ScanEngine + // which will then generate an object reference for it. + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + else + scanEngine.writeObject(args[i]); + } + + Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs); + + try + { + newStmt.execute(); + } + catch (Exception e) + { + getExceptionListener().exceptionThrown(e); + + // In Statement.writeStatement we had no possibility to flags + // an erroneous state to the ScanEngine without behaving different + // to the JDK. + scanEngine.revoke(); + return; + } + + scanEngine.end(); + } + + public void writeObject(Object o) + { + accessCounter++; + + scanEngine.writeObject(o); + + if (get(o) == null) + super.writeObject(o); + + accessCounter--; + } + + public void setOwner(Object o) + { + owner = o; + } + + public Object getOwner() + { + return owner; + } + +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContext.java b/libjava/classpath/java/beans/beancontext/BeanContext.java new file mode 100644 index 000000000..803cb36ff --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContext.java @@ -0,0 +1,272 @@ +/* java.beans.beancontext.BeanContext + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.DesignMode; +import java.beans.Visibility; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collection; + +/** + * Acts as a container for sub-beans and as a sub-bean, + * so that an entire hierarchy of beans can be made up of + * <code>BeanContext</code>s. + * <P> + * + * Since I can't sprinkle the <code>Collections</code> interface + * documentation with special information for <code>BeanContext</code> + * implementors, I'll have to document special requirements for + * implementors of those functions here. + * <P> + * + * <code><strong>add()</strong></code> or <code>addAll()</code>: + * <br> + * <OL> + * <LI> + * May add any <code>Object</code> into the hierarchy as well as a + * <code>BeanContextChild</code>, <code>BeanContext</code> or + * <code>BeanContextProxy</code> object. + * This way, any Bean can be in the hierarchy. + * </LI> + * <LI> + * Must synchronize on <code>BeanContext.globalHierarchyLock</code>. + * </LI> + * <LI> + * Don't add the <code>Object</code> if it's already there (only once + * per <code>BeanContext</code>). + * </LI> + * <LI> + * If it is a <code>BeanContextChild</code> implementor, call + * <code>setBeanContext()</code> on it. If it's a + * <code>BeanContextProxy</code> implementor, call + * <code>getBeanContextProxy().setBeanContext()</code> on it. + * If <code>setBeanContext()</code> vetoes the change, back out + * all changes so far and throw <code>IllegalStateException</code>. + * </LI> + * <LI> + * If it (or its proxy) implements <code>Visibility</code>, call + * <code>dontUseGui()</code> or <code>okToUseGui()</code> on it, + * depending on whether you (the <code>BeanContext</code>) feel like + * allowing it to use the GUI or not. + * </LI> + * <LI> + * If it implements <code>BeanContextChild</code> or + * <code>BeanContextProxy</code>, register yourself (the + * <code>BeanContext</code>) as both a + * <code>PropertyChangeListener</code> and + * <code>VetoableChangeListener</code> on the "beanContext" + * property (it may also add itself on any other properties it wishes + * to). + * </LI> + * <LI> + * If it is a listener or event source that you (the + * <code>BeanContext</code>) are interested in, you may register + * yourself to it or register it to you. + * </LI> + * <LI> + * Fire a <code>java.beans.beancontext.BeanContextMembershipEvent</code> + * before exiting. <code>addAll()</code> should wait until everything + * is done changing before firing the event (or events) so that if a + * failure occurs, the backing-out process can proceed without any + * events being fired at all. + * </LI> + * </OL> + * <P> + * + * <code><strong>remove()</strong></code> or <code>removeAll()</code>: + * <br> + * <OL> + * <LI> + * Must synchronize on <code>BeanContext.globalHierarchyLock</code>. + * </LI> + * <LI> + * If the specified <code>Object</code> is not a child of this + * <code>BeanContext</code>, just exit without performing any actions. + * </LI> + * <LI> + * Remove the <code>Object</code> from your collection of children. + * </LI> + * <LI> + * If it is a <code>BeanContextChild</code> implementor, call + * <code>setBeanContext(null)</code> on it. If it's a + * <code>BeanContextProxy</code> implementor, call + * <code>getBeanContextProxy().setBeanContext(null)</code> on it. + * If <code>setBeanContext()</code> vetoes the change, back out + * all changes so far and throw <code>IllegalStateException</code>. + * </LI> + * <LI> + * If you registered the <code>Object</code> to listen to you or + * registered yourself as a listener on the <code>Object</code> during + * <code>add()</code> or <code>addAll()</code>, undo the registration + * bycalling the appropriate <code>removeListener()</code> method. + * </LI> + * <LI> + * Fire a <code>java.beans.beancontext.BeanContextMembershipEvent</code> + * before exiting. <code>removeAll()</code> should wait until + * everything is done changing before firing the event (or events) so + * that if a failure occurs, the backing-out process can proceed + * without any events being fired at all. + * </LI> + * </OL> + * <P> + * + * <code>addAll()</code>, <code>removeAll()</code>, + * <code>retainAll()</code> and <code>clear()</code> do not need to be + * implemented, but may be if so desired. + * <P> + * + * Similarly, <code>Visibility</code> and <code>DesignMode</code> methods + * should propagate changed values to children that implement interfaces + * of the same name. + * <P> + * + * A hierarchy of beans is mainly useful so that different sets of beans + * can be established, each with their own set of resources. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContext + extends Collection, BeanContextChild, Visibility, DesignMode { + + /** + * The global lock on changing any BeanContext hierarchy. + * It kinda sucks that there is only one lock, since there can be + * multiple hierarchies. Oh well, I didn't design, I just code. + * <P> + * + * Methods that must (or do) synchronize on the global lock: + * <BR> + * <UL> + * <LI> + * Implementors of <CODE>BeanContext.add()</CODE> and <code>addAll()</code> + * </LI> + * </UL> + * @fixme fill in the rest of the methods which use the global lock. + */ + Object globalHierarchyLock = new Object(); + + /** + * Instantiate a Bean using this Bean's <code>ClassLoader</code> + * and this <code>BeanContext</code> as the parent. + * <P> + * + * This method exists mainly so that <code>BeanContext</code> + * implementations can perform extra actions on Beans that are + * created within them. + * + * @param beanName the name of the bean to instantiate + * @return the created Bean + * + * @see java.beans.Beans#instantiate(java.lang.ClassLoader,java.lang.String) + * @see java.beans.Beans#instantiate(java.lang.ClassLoader,java.lang.String,java.beans.beancontext.BeanContext) + * @exception IOException if there is an I/O problem during + * instantiation. + * @exception ClassNotFoundException if a serialized Bean's class + * is not found. + */ + Object instantiateChild(String beanName) + throws IOException, + ClassNotFoundException; + + /** + * Get a resource. The <code>BeanContext</code> will typically + * call <code>ClassLoader.getResource()</code>, but may do it any + * way it wants to. This allows a <code>BeanContext</code> to + * have its own set of resources separate from the rest of the + * system. + * <P> + * + * Beans should call this method on their parent rather than the + * associated <code>ClassLoader</code> method. + * <P> + * + * I am assuming, but am not entirely sure, that if a + * <code>BeanContext</code> cannot find a resource, its + * responsibility is to call the <code>getResource</code> method + * of its parent <code>BeanContext</code>. + * + * @return a URL to the requested resource. + * @param resourceName the name of the resource requested. + * @param requestor a reference to the child requesting the resource. + * @see java.lang.ClassLoader#getResource(java.lang.String) + */ + URL getResource(String resourceName, BeanContextChild requestor); + + /** + * Get a resource as a stream. The <code>BeanContext</code> will + * typically call <code>ClassLoader.getResourceAsStream()</code>, + * but may do it any way it wants to. This allows a + * <code>BeanContext</code>'s children to have their own set of + * resources separate from the rest of the system. + * <P> + * + * Beans should call this method on their parent rather than the + * associated <code>ClassLoader</code> method. + * <P> + * + * I am assuming, but am not entirely sure, that if a + * <code>BeanContext</code> cannot find a resource, its + * responsibility is to call the <code>getResourceAsStream</code> + * method of its parent <code>BeanContext</code>. + * + * @return the requested resource as a stream. + * @param resourceName the name of the resource requested. + * @param requestor a reference to the child requesting the resource. + * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String) + */ + InputStream getResourceAsStream(String resourceName, BeanContextChild requestor); + + /** + * Add a listener on changes to the membership of this + * <code>BeanContext</code> object. + * @param listener the listener to add. + */ + void addBeanContextMembershipListener(BeanContextMembershipListener listener); + + /** + * Remove a listener on changes to the membership of this + * <code>BeanContext</code> object. + * @param listener the listener to remove. + */ + void removeBeanContextMembershipListener(BeanContextMembershipListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChild.java b/libjava/classpath/java/beans/beancontext/BeanContextChild.java new file mode 100644 index 000000000..e2bdcf336 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChild.java @@ -0,0 +1,174 @@ +/* java.beans.beancontext.BeanContextChild + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +/** + * Beans implement this to get information about the execution environment and + * its services and to be placed in the hierarchy. + * <P> + * + * The difference between a <code>BeanContext</code> and a + * <code>BeanContextChild</code>, mainly, is that a + * <code>BeanContext</code> may be a parent. + * <P> + * + * <code>BeanContextChild</code> instances will be serialized at some + * point in their life, but you need to make sure your bean context does + * not contain a serializable reference (directly or indirectly) to the + * parent <code>BeanContext</code>, to any of the other + * <code>BeanContext</code>s in the tree, or to any resources obtained + * via the <code>BeanContextServices</code> interface. One way to do this + * is to mark any fields that contain such references as + * <code>transient</code>. Another way is to use a custom serializer. + * <P> + * + * If you do not do this, when the <code>BeanContext</code> is serialized, + * all the other <code>BeanContext</code>s and other unnecessary things + * will be serialized along with it. + * <P> + * + * Before dying, a <code>BeanContextChild</code> should call + * <code>getBeanContext().remove(this)</code> to detach from the + * hierarchy and exit cleanly. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContext + */ + +public interface BeanContextChild { + /** + * Set the parent <code>BeanContext</code>. + * <P> + * + * This method is called from <code>BeanContext.add()</code> and + * should not be called directly. + * <P> + * + * When this Object is being added to a new BeanContext or moved + * from an old one, a non-null value will be passed in. + * <P> + * + * When this Object is being removed from the current + * <code>BeanContext</code>, <code>setBeanContext()</code> will + * receive the parameter <code>null</code>. + * <P> + * + * When being removed from the current <code>BeanContext</code>, + * it is the <code>BeanContextChild</code>'s responsibility to + * release all services it has obtained. + * <P> + * + * This change should generate <code>PropertyChangeEvent</code> + * and <code>VetoableChangeEvent</code>s with the property name + * "beanContext". If the change is vetoed, it must re-throw the + * exception and not change anything. In this way, the parent + * <code>BeanContextChild</code>, who has registered himself with + * you, will have a chance to remove this child from its + * collection. + * <P> + * + * If the Bean does not wish to change the parent or be removed + * from one, it may throw the <code>PropertyVetoException</code>. + * If you veto a <code>setBeanContext(null)</code> call, then you + * should try your hardest to remedy whatever problem is keeping + * you from being removed from the <code>BeanContext</code> so + * that you can <em>not</em> veto it the next time. + * Otherwise, nasty pathological recursion stuff could occur in + * certain situations. + * <P> + * + * If you do veto the change, you must first back out any changes + * you made prior to the veto. Best not to make any such changes + * prior to the veto in the first place. + * <P> + * + * This method is called from <code>BeanContext.add()</code> and + * should not be called directly. + * + * @param parent the new parent for the <code>BeanContextChild</code>, + * or <code>null</code> to signify removal from a tree. + * @exception PropertyVetoException if the + * <code>BeanContextChild</code> implementor does not + * wish to have its parent changed. + */ + void setBeanContext(BeanContext parent) + throws PropertyVetoException; + + /** + * Get the parent <code>BeanContext</code>. + * @return the parent <code>BeanContext</code>. + */ + BeanContext getBeanContext(); + + /** + * Add a listener that will be notified when a specific property changes. + * @param prop the name of the property to listen on + * @param listener the listener to listen on the property. + */ + void addPropertyChangeListener(String prop, PropertyChangeListener listener); + + /** + * Remove a listener to a certain property. + * @param prop the name of the property being listened on + * @param listener the listener listening on the property. + */ + void removePropertyChangeListener(String prop, PropertyChangeListener listener); + + /** + * Add a listener that will be notified when a specific property + * change is requested (a PropertyVetoException may be thrown) as + * well as after the change is successfully made. + * + * @param prop the name of the property to listen on + * @param listener the listener to listen on the property. + */ + void addVetoableChangeListener(String prop, VetoableChangeListener listener); + + /** + * Remove a listener to a certain property. + * @param prop the name of the property being listened on + * @param listener the listener listening on the property. + */ + void removeVetoableChangeListener(String prop, VetoableChangeListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java new file mode 100644 index 000000000..cb75d508d --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java @@ -0,0 +1,60 @@ +/* java.beans.beancontext.BeanContextChildComponentProxy + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.awt.Component; + +/** + * Interface for <code>BeanContextChild</code>s which wish to associate an + * AWT component with them. The proxy is provided because the + * <code>addPropertyChangeListener()</code> method would conflict with + * <code>Component</code> if you tried to extend. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextChildComponentProxy { + /** + * Get the <code>Component</code> associated with this <code>BeanContextChild</code>. + * @return the <code>Component</code> associated with this + * <code>BeanContextChild</code>. + */ + Component getComponent(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java new file mode 100644 index 000000000..8cd887d0c --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java @@ -0,0 +1,381 @@ +/* java.beans.beancontext.BeanContextChildSupport + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.Serializable; + +/** + * Support for creating a <code>BeanContextChild</code>. + * This class contains the most common implementations of the methods in + * the <code>BeanContextChild</code> + * + * @specnote This class is not very well specified. I had to "fill in the + * blanks" in most places with what I thought was reasonable + * behavior. If there are problems, let me know. + * + * @author John Keiser + * @since 1.2 + * @see java.beans.beancontext.BeanContextChild + */ +public class BeanContextChildSupport + implements BeanContextChild, BeanContextServicesListener, Serializable +{ + static final long serialVersionUID = 6328947014421475877L; + + /** + * The peer on which to perform <code>set</code> actions. + * This is here so that this class can be used as a peer. + * <P> + * + * When extending this class, this variable will be set to + * <code>this</code>. + */ + public BeanContextChild beanContextChildPeer; + + /** + * The parent <code>BeanContext</code>. + */ + protected transient BeanContext beanContext; + + /** + * If <code>setBeanContext()</code> was vetoed once before, this + * is set to <code>true</code> so that the next time, vetoes will + * be ignored. + */ + protected transient boolean rejectedSetBCOnce; + + /** + * Listeners are registered here and events are fired through here. + */ + protected PropertyChangeSupport pcSupport; + + /** + * Listeners are registered here and events are fired through here. + */ + protected VetoableChangeSupport vcSupport; + + /** + * Create a new <code>BeanContextChildSupport</code> with itself as the peer. + * This is meant to be used when you subclass + * <code>BeanContextChildSupport</code> to create your child. + */ + public BeanContextChildSupport() + { + this (null); + } + + /** + * Create a new <code>BeanContextChildSupport</code> with the specified peer. + * @param peer the peer to use, or <code>null</code> to specify + * <code>this</code>. + */ + public BeanContextChildSupport (BeanContextChild peer) + { + if (peer == null) + { + peer = this; + } + + beanContextChildPeer = peer; + pcSupport = new PropertyChangeSupport (peer); + vcSupport = new VetoableChangeSupport (peer); + } + + /** + * Set the parent <code>BeanContext</code>. + * <P> + * + * When this Object is being added to a new BeanContext or moved + * from an old one, a non-null value will be passed in. + * <P> + * + * When this Object is being removed from the current + * <code>BeanContext</code>, <code>setBeanContext()</code> will + * receive the parameter <code>null</code>. + * <P> + * + * Order of events: + * <OL> + * <LI> + * If the new <code>BeanContext</code> is the same as the old + * one, nothing happens. + * </LI> + * <LI> + * If the change has not been rejected or vetoed before, call + * <code>validatePendingSetBeanContext()</code>. If this call + * returns <code>false</code>, the change is rejected and a + * <code>PropertyVetoException</code> is thrown. + * </LI> + * <LI> + * If the change has not been rejected or vetoed before, + * <code>VetoableChangeEvent</code>s are fired with the name + * <code>"beanContext"</code>, using the + * <code>fireVetoableChange()</code> method. If a veto + * occurs, reversion events are fired using the same method, + * the change is rejected, and the veto is rethrown. + * </LI> + * <LI> + * <code>releaseBeanContextResources()</code> is called. + * </LI> + * <LI> + * The change is made. + * </LI> + * <LI> + * <code>PropertyChangeEvent</code>s are fired using the + * <code>firePropertyChange()</code> method. + * </LI> + * <LI> + * <code>initializeBeanContextResources()</code> is called. + * </LI> + * </OL> + * <P> + * + * @param newBeanContext the new parent for the + * <code>BeanContextChild</code>, or <code>null</code> to + * signify removal from a tree. + * @exception PropertyVetoException if the + * <code>BeanContextChild</code> implementor does not + * wish to have its parent changed. + */ + public void setBeanContext(BeanContext newBeanContext) + throws PropertyVetoException + { + synchronized (beanContextChildPeer) + { + if (newBeanContext == beanContext) + return; + + if (!rejectedSetBCOnce) + { + if (!validatePendingSetBeanContext (newBeanContext)) + { + rejectedSetBCOnce = true; + throw new PropertyVetoException ("validatePendingSetBeanContext() rejected change", + new PropertyChangeEvent(beanContextChildPeer, "beanContext", beanContext, newBeanContext)); + } + + try + { + fireVetoableChange ("beanContext", beanContext, newBeanContext); + } + catch (PropertyVetoException e) + { + rejectedSetBCOnce = true; + throw e; + } + } + + releaseBeanContextResources (); + + beanContext = newBeanContext; + rejectedSetBCOnce = false; + + firePropertyChange ("beanContext", beanContext, newBeanContext); + + initializeBeanContextResources (); + } + } + + /** + * Get the parent <code>BeanContext</code>. + * @return the parent <code>BeanContext</code>. + */ + public BeanContext getBeanContext() + { + return beanContext; + } + + /** + * Get the peer (or <code>this</code> if there is no peer). + * @return the peer, or <code>this</code> if there is no peer. + */ + public BeanContextChild getBeanContextChildPeer() { + return beanContextChildPeer; + } + + /** + * Determine whether there is a peer. + * This is true iff <code>getBeanContextChildPeer() == this</code>. + * @return whether there is a peer. + */ + public boolean isDelegated() { + return beanContextChildPeer == this; + } + + /** + * Add a listener that will be notified when a specific property changes. + * @param propertyName the name of the property to listen on. + * @param listener the listener to listen on the property. + */ + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcSupport.addPropertyChangeListener(propertyName, listener); + } + + /** + * Remove a listener to a certain property. + * + * @param propertyName the name of the property being listened on. + * @param listener the listener listening on the property. + */ + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcSupport.removePropertyChangeListener(propertyName, listener); + } + + /** + * Add a listener that will be notified when a specific property + * change is requested (a PropertyVetoException may be thrown) as + * well as after the change is successfully made. + * + * @param propertyName the name of the property to listen on. + * @param listener the listener to listen on the property. + */ + public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) { + vcSupport.addVetoableChangeListener(propertyName, listener); + } + + /** + * Remove a listener to a certain property. + * + * @param propertyName the name of the property being listened on + * @param listener the listener listening on the property. + */ + public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) { + vcSupport.removeVetoableChangeListener(propertyName, listener); + } + + /** + * Fire a property change. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value of the property + * @param newVal the new value of the property + */ + public void firePropertyChange(String propertyName, Object oldVal, Object newVal) { + pcSupport.firePropertyChange(propertyName, oldVal, newVal); + } + + /** + * Fire a vetoable property change. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value of the property + * @param newVal the new value of the property + * @exception PropertyVetoException if the change is vetoed. + */ + public void fireVetoableChange(String propertyName, Object oldVal, Object newVal) + throws PropertyVetoException { + vcSupport.fireVetoableChange(propertyName, oldVal, newVal); + } + + /** + * Called by <code>BeanContextServices.revokeService()</code> to indicate that a service has been revoked. + * If you have a reference to such a service, it should be + * discarded and may no longer function properly. + * <code>getService()</code> will no longer work on the specified + * service class after this event has been fired. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + * + * @param event the service revoked event. + * @see java.beans.beancontext.BeanContextServices#revokeService(java.lang.Class,java.beans.beancontext.BeanContextServiceProvider,boolean) + */ + public void serviceRevoked(BeanContextServiceRevokedEvent event) { + } + + /** + * Called by <code>BeanContextServices</code> whenever a service is made available. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + * + * @param event the service revoked event, with useful information + * about the new service. + */ + public void serviceAvailable(BeanContextServiceAvailableEvent event) { + } + + /** + * Called by <code>setBeanContext()</code> to determine whether the set should be rejected. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation simply + * returns <code>true</code>. + * + * @param newBeanContext the new parent. + * @return whether to allow the parent to be changed to the new + * value. + */ + public boolean validatePendingSetBeanContext(BeanContext newBeanContext) { + return true; + } + + /** + * Called by <code>setBeanContext()</code> to release resources of a what will soon no longer be the parent. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + */ + protected void releaseBeanContextResources() { + } + + /** + * Called by <code>setBeanContext()</code> to grab resources when the parent has been set. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + */ + protected void initializeBeanContextResources() { + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java new file mode 100644 index 000000000..962cb5fc9 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java @@ -0,0 +1,63 @@ +/* java.beans.beancontext.BeanContextContainerProxy + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.awt.Container; + +/** + * Interface for <code>BeanContext</code>s which wish to associate an + * AWT container with them. The proxy is provided because the + * <code>addPropertyChangeListener()</code> and <code>add()</code> methods + * would conflict with <code>Component</code> and <code>Container</code> + * if you tried to extend. + * + * @specnote It is unclear whether anything besides <code>BeanContext</code>s + * are allowed to implement this interface. + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextContainerProxy { + /** + * Get the <code>Container</code> associated with this <code>BeanContext</code>. + * @return the <code>Container</code> associated with this + * <code>BeanContext</code>. + */ + Container getContainer(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextEvent.java new file mode 100644 index 000000000..959d54e58 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextEvent.java @@ -0,0 +1,110 @@ +/* java.beans.beancontext.BeanContextEvent + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventObject; + +/** + * Generic superclass for events fired by <code>BeanContext</code>s. + * + * @author John Keiser + * @since 1.2 + */ + +public abstract class BeanContextEvent extends EventObject +{ + private static final long serialVersionUID = 7267998073569045052L; + + /** + * The <code>BeanContext</code> that most recently passed this + * event on. + */ + protected BeanContext propagatedFrom; + + /** + * Create a new event, from the specified <code>BeanContext</code>. + * <code>propagatedFrom</code> will be initialized to + * <code>null</code>. + * + * @param source the source of the event. + */ + protected BeanContextEvent(BeanContext source) + { + super(source); + } + + /** + * Get the <code>BeanContext</code> that originated this event. + * @return the originator of this event. + */ + public BeanContext getBeanContext() + { + return (BeanContext)getSource(); + } + + /** + * Get the most recent propagator of this event. + * If this value is <code>null</code>, you have received the event + * straight from the source. + * + * @return the most recent propagator of this event. + */ + public BeanContext getPropagatedFrom() + { + return propagatedFrom; + } + + /** + * Tell whether this event has been propagated. + * @return <code>true</code> iff <code>getPropagatedFrom() != null</code>. + */ + public boolean isPropagated() + { + return propagatedFrom != null; + } + + /** + * Set the most recent propagator of this event. + * @param propagator the most recent propagator of this event. + */ + public void setPropagatedFrom(BeanContext propagator) + { + propagatedFrom = propagator; + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java new file mode 100644 index 000000000..77b1be43e --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java @@ -0,0 +1,114 @@ +/* java.beans.beancontext.BeanContextMembershipEvent + Copyright (C) 1999, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +/** + * Event fired when children are added to or removed from a <code>BeanContext</code>. + * Whether they were added or removed depends entirely on which method + * of the listener interface was called. + * + * @author John Keiser + * @since 1.2 + * @see java.beans.beancontext.BeanContextMembershipListener + */ +public class BeanContextMembershipEvent extends BeanContextEvent { + private static final long serialVersionUID = 3499346510334590959L; + + /** + * The children that were added or removed. + */ + protected Collection children; + + /** + * Create a new membership event. + * @param context the event source. + * @param children the children added to or removed from the source. + */ + public BeanContextMembershipEvent(BeanContext context, Collection children) { + super(context); + this.children = children; + } + + /** + * Create a new membership event. + * @param context the event source. + * @param children the children added to or removed from the source. + */ + public BeanContextMembershipEvent(BeanContext context, Object[] children) { + super(context); + this.children = Arrays.asList(children); + } + + /** + * The number of children removed or added. + * @return the number of children removed or added. + */ + public int size() { + return children.size(); + } + + /** + * An iterator that will step through all the children. + * @return an iterator over all the children. + */ + public Iterator iterator() { + return children.iterator(); + } + + /** + * An array of the children. + * @return an array of the children. + */ + public Object[] toArray() { + return children.toArray(); + } + + /** + * Tell whether the <code>Object</code> is one of the children added or removed. + * @param child the child to check. + * @return whether the <code>Object</code> is added or removed. + */ + public boolean contains(Object child) { + return children.contains(child); + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java b/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java new file mode 100644 index 000000000..cdb47c811 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java @@ -0,0 +1,70 @@ +/* java.beans.beancontext.BeanContextMembershipListener + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventListener; + +/** + * This is the interface to which <code>BeanContextMembershipEvent</code>s are sent. + * This happens when children are added to or removed from a + * <code>BeanContext</code>. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextMembershipListener extends EventListener { + /** + * When beans are added to a <code>BeanContext</code>, + * this method is called to fire the event. + * + * @param event the event, including which children were added. + * @see java.beans.beancontext.BeanContext#add(java.lang.Object) + */ + void childrenAdded(BeanContextMembershipEvent event); + + /** + * When beans are removed from a <code>BeanContext</code>, + * this method is called to fire the event. + * + * @param event the event, including which children were removed. + * @see java.beans.beancontext.BeanContext#remove(java.lang.Object) + */ + void childrenRemoved(BeanContextMembershipEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextProxy.java new file mode 100644 index 000000000..53632e86b --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextProxy.java @@ -0,0 +1,65 @@ +/* java.beans.beancontext.BeanContextProxy + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Beans that wish to have a <code>BeanContextChild</code> or <code>BeanContext</code> associated with them + * but do not wish to implement those interfaces directly, can implement this interface. + * <P> + * + * Don't shoot yourself in the foot: if you already implement + * <code>BeanContextChild</code>, directly or indirectly, the whole + * workings of this package will be unpredictable because it is + * indeterminate as to whether the <code>BeanContextChild</code> is used + * in preference to its proxy or vice versa. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextProxy { + /** + * Return the <code>BeanContextChild</code> associated with this + * <code>Object</code>. + * + * @return the <code>BeanContextChild</code> associated with this + * <code>Object</code>. + */ + BeanContextChild getBeanContextProxy(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java new file mode 100644 index 000000000..a2bdcdde7 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java @@ -0,0 +1,97 @@ +/* java.beans.beancontext.BeanContextServiceAvailableEvent + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; + +/** + * Event fired when new services become available through a <code>BeanContextServices</code>. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextServicesListener + */ + +public class BeanContextServiceAvailableEvent extends BeanContextEvent { + private static final long serialVersionUID = -5333985775656400778L; + + /** + * The <code>Class</code> representing the service which is now + * available. + */ + protected Class serviceClass; + + /** + * Create a new service available event. + * @param services the <code>BeanContextServices</code> through + * which the service is available. This is also the source + * of the event. + * @param serviceClass the service class that is now available. + */ + public BeanContextServiceAvailableEvent(BeanContextServices services, Class serviceClass) { + super(services); + this.serviceClass = serviceClass; + } + + /** + * Get the current service selectors of the service class. + * This is identical to <code>getSourceAsBeanContextServices().getCurrentServiceSelectors(getServiceClass())</code> + * @return the current service selectors of the service class. + */ + public Iterator getCurrentServiceSelectors() { + return getSourceAsBeanContextServices().getCurrentServiceSelectors(serviceClass); + } + + /** + * Get the newly available service class. + * @return the service class. + */ + public Class getServiceClass() { + return serviceClass; + } + + /** + * Get the <code>BeanContextServices</code> through which the new service is available. + * @return the <code>BeanContextServices</code> through which the + * new service is available. + */ + public BeanContextServices getSourceAsBeanContextServices() { + return (BeanContextServices)getSource(); + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java new file mode 100644 index 000000000..7475a61bd --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java @@ -0,0 +1,138 @@ +/* java.beans.beancontext.BeanContextServiceProvider + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; + +/** + * An actual factory for services. + * <P> + * + * It is the <code>BeanContextServiceProvider</code>'s responsibility to + * register itself with whatever <code>BeanContextServices</code> object + * it wishes to provide services through using the + * <code>addService()</code> method. + * <P> + * + * If for some reason it can no longer provide services for a particular + * class, this class must invoke + * <code>BeanContextServices.revokeService(serviceClass,this,true)</code> + * for all the places it has registered the service. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServiceProvider { + /** + * Get a service. + * Called from <code>BeanContextServices.getService()</code>. + * + * <p>If the requested service class is not available, or if this + * <code>BeanContextServiceProvider</code> chooses not honor the + * request for some reason, then this method will return + * <code>null</code>.</p> + * + * This method may throw unchecked exceptions, so watch out. + * + * @param services the <code>BeanContextServices</code> that wants + * to get the service. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param requestor the actual requestor of the service. Only + * weak references to this will be retained, and it will + * never be changed, only queried in a read-only manner. + * @param serviceClass the <code>Class</code> of the service being + * requested. + * @param serviceSelector a parameter to customize the service + * returned with. + * @return an instance of <code>serviceClass</code> (such that + * <code>instanceof</code> serviceClass is true), or + * <code>null</code>. + * @see java.beans.beancontext.BeanContextServices#getService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Class,java.lang.Object,java.beans.beancontext.BeanContextServiceRevokedListener) + */ + Object getService(BeanContextServices services, Object requestor, Class serviceClass, Object serviceSelector); + + /** + * Release the service. + * <P> + * + * Called by <code>BeanContextServices.releaseService()</code>. + * <P> + * + * Most <code>BeanContextServiceProvider</code>s won't have to do + * anything here. + * + * @param services the <code>BeanContextServices</code> that wants + * to release the service. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param requestor the original requestor of the service. + * @param service the service to relinquish + * @see java.beans.beancontext.BeanContextServices#releaseService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Object) + */ + void releaseService(BeanContextServices services, Object requestor, Object service); + + /** + * Get a list of valid service selectors for the specified service class. + * This method is called from + * <code>BeanContextServices.getCurrentServiceSelectors()</code>. + * <P> + * + * If the specified service class does not have a finite number of + * valid service selectors, it should return <code>null</code>. + * If it takes a general <code>Integer</code> parameter, for + * example, you may as well return <code>null</code> or the poor + * soul who called this method will be iterating all day. + * <P> + * + * If it has no valid service selectors, it should still return an empty + * <code>Iterator</code>. + * + * @param services the <code>BeanContextServices</code> that wants + * to get the service selectors. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param serviceClass the service class to get selectors for. + * @return a list of valid service selectors for the service + * class, or <code>null</code>. + * @see java.beans.beancontext.BeanContextServices#getCurrentServiceSelectors(java.lang.Class) + */ + Iterator getCurrentServiceSelectors(BeanContextServices services, Class serviceClass); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java new file mode 100644 index 000000000..78bfc200d --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java @@ -0,0 +1,60 @@ +/* java.beans.beancontext.BeanContextServiceProviderBeanInfo + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.BeanInfo; + +/** + * <code>BeanContextServiceProvider</code>s implement this to provide information about all of the services they provide. + * <P> + * + * This is apparently so that you can import a bunch of services into a + * RAD tool and it will know about all of them and export them to the + * user in a readable manner. + * + * @author John Keiser + * @since JDK1.2 + */ +public interface BeanContextServiceProviderBeanInfo extends BeanInfo { + /** + * Get <code>BeanInfo</code>s for all of the service classes of this <code>BeanInfoServiceProvider</code>. + * @return <code>BeanInfo</code>s for all provided service classes. + */ + BeanInfo[] getServicesBeanInfo(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java new file mode 100644 index 000000000..b4f2fa856 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java @@ -0,0 +1,112 @@ +/* java.beans.beancontext.BeanContextServiceRevokedEvent + Copyright (C) 1999, 2000 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Event fired when services are revoked from a <code>BeanContextServices</code>. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextServiceRevokedListener + */ + +public class BeanContextServiceRevokedEvent extends BeanContextEvent { + private static final long serialVersionUID = -1295543154724961754L; + + /** + * The <code>Class</code> representing the service which is now + * available. + */ + protected Class serviceClass; + private boolean invalidateRefs; + + /** + * Create a new service revoked event. + * @param services the <code>BeanContextServices</code> through + * which the service was available. This is also the source + * of the event. + * @param serviceClass the service class that is now revoked. + * @param revokeNow whether the revocation is immediate for all + * classes or just a suggestion. + */ + public BeanContextServiceRevokedEvent(BeanContextServices services, Class serviceClass, boolean revokeNow) { + super(services); + this.serviceClass = serviceClass; + invalidateRefs = revokeNow; + } + + /** + * Get the revoked service class. + * @return the service class. + */ + public Class getServiceClass() { + return serviceClass; + } + + /** + * Tell whether the revoked service class is the same as the specified class. + * Identical to <code>getServiceClass().equals(c)</code>. + * @param c the class to compare. + * @return whether the clases are equal. + */ + public boolean isServiceClass(Class c) { + return serviceClass.equals(c); + } + + /** + * Get the <code>BeanContextServices</code> through which the service was available. + * @return the <code>BeanContextServices</code> through which the + * service was available. + */ + public BeanContextServices getSourceAsBeanContextServices() { + return (BeanContextServices)getSource(); + } + + /** + * Tell whether current instances of the revoked service are usable or not. + * This is determined by whether the service was revoked + * immediately. + * + * @return whether current instances of the revoked service are + * usable. + */ + public boolean isCurrentServiceInvalidNow() { + return invalidateRefs; + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java new file mode 100644 index 000000000..11b83dd65 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java @@ -0,0 +1,62 @@ +/* java.beans.beancontext.BeanContextServiceRevokedListener + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventListener; + +/** + * Listens for service revoke events. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServiceRevokedListener extends EventListener { + /** + * Called by <code>BeanContextServices.revokeService()</code> to indicate that a service has been revoked. + * If you have a reference to such a service, it should be + * discarded and may no longer function properly. + * <code>getService()</code> will no longer work on the specified + * service class after this event has been fired. + * + * @param event the service revoked event. + * @see java.beans.beancontext.BeanContextServices#revokeService(java.lang.Class,java.beans.beancontext.BeanContextServiceProvider,boolean) + */ + void serviceRevoked(BeanContextServiceRevokedEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServices.java b/libjava/classpath/java/beans/beancontext/BeanContextServices.java new file mode 100644 index 000000000..787618ad5 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServices.java @@ -0,0 +1,216 @@ +/* java.beans.beancontext.BeanContextServices + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; +import java.util.TooManyListenersException; + +/** + * Allows a <code>BeanContext</code> to provide services to its children. + * + * @specnote it is unclear whether a <code>BeanContextServices</code> + * should delegate unhandled requests to parents. I assume so. + * @author John Keiser + * @since 1.2 + */ + +public interface BeanContextServices + extends BeanContext, BeanContextServicesListener +{ + /** + * Register a service to make it available to others. + * This class may refuse to add the service based on whatever + * information it can gather, including whether the service + * provider is trusted. + * + * @param serviceClass the service class. + * @param provider the factory that will actually provide the service. + * @return whether the service was added or not. + */ + boolean addService (Class serviceClass, + BeanContextServiceProvider provider); + + /** + * Make it so that no one else can use this service. + * <P> + * + * If <code>revokeNow</code> is <code>false</code>, the only + * effect of this method is to make all subsequent calls to + * <code>getService()</code> on this service class fail. + * <P> + * + * If it is <code>true</code>, a message is also sent out to all + * listeners on the service and all references to it are released. + * + * @param serviceClass the service class to revoke. + * @param provider the service provider providing the service class. + * @param revokeNow whether to release all current references to + * the service. + */ + void revokeService (Class serviceClass, + BeanContextServiceProvider provider, + boolean revokeNow); + + /** + * Release your copy of this service. + * <P> + * + * If all copies of the service's class have been relinquished by + * the requestor, the <code>BeanContextServiceRevokedListener</code> + * previously registered by <code>getService()</code> will be + * unregistered. + * + * @param requestorChild the original <code>BeanContextChild</code> + * requesting the service. + * @param requestor the original requestor of the service. + * @param service the service to relinquish + * @see #getService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Class,java.lang.Object,java.beans.beancontext.BeanContextServiceRevokedListener) + */ + void releaseService (BeanContextChild requestorChild, Object requestor, + Object service); + + /** + * Get a service from this <code>BeanContextServices</code>. + * <P> + * + * The specified listener will be registered to receive a + * revocation notice for the specified serviceClass. One + * notification per service class per requestor object will be + * sent. + * <P> + * + * The listener will be unregistered when all services that were + * obtained by that requestor for that service class are released. + * <P> + * + * If the requested service class is not available, or if this + * <code>BeanContextServices</code> object chooses not honor the + * request because the service class has been revoked or for some + * other reason, then this method will return <code>null</code>. + * <P> + * + * This method may throw unchecked exceptions, so watch out. + * + * @specnote it is not specified what happens when two subsequent + * calls are made to <code>getService()</code> with the + * same requestor object and service class but different + * listeners. Which listener is to be notified? + * + * @param requestorChild the <code>BeanContextChild</code> + * associated with the requestor. Typically this will be + * the same as the requestor itself, but since any + * <code>Object</code>, even one outside the hierarchy, may + * make a request, this parameter is necessary. Only weak + * references to this will be retained, and it will never + * be changed, only queried in a read-only manner. + * @param requestor the actual requestor of the service. Only + * weak references to this will be retained, and it will + * never be changed, only queried in a read-only manner. + * @param serviceClass the <code>Class</code> of the service being + * requested. + * @param serviceSelector a parameter to customize the service + * returned with. + * @param listener a listener that will be notified if the service + * being requested is revoked. + * @return an instance of <code>serviceClass</code> (such that + * <code>instanceof</code> serviceClass is true), or + * <code>null</code>. + */ + Object getService (BeanContextChild requestorChild, Object requestor, + Class serviceClass, Object serviceSelector, + BeanContextServiceRevokedListener listener) + throws TooManyListenersException; + + /** + * Get a list of all service classes supported. + * <P> + * + * This method must synchronize on + * <code>BeanContext.globalHierarchyLock</code>. + * + * @return a list of all service classes supported. + * @see java.beans.beancontext.BeanContext#globalHierarchyLock + */ + Iterator getCurrentServiceClasses (); + + /** + * Get a list of valid service selectors for the specified service class. + * <P> + * + * If the specified service class does not have a finite number of + * valid service selectors, it should return <code>null</code>. + * If it takes a general <code>Integer</code> parameter, for + * example, you may as well return <code>null</code> or the poor + * soul who called this method will be iterating all day. + * <P> + * + * If it has no valid service selectors, it should still return an empty + * <code>Iterator</code>. + * + * @param serviceClass the service class to get selectors for. + * @return a list of valid service selectors for the service + * class, or <code>null</code>. + */ + Iterator getCurrentServiceSelectors (Class serviceClass); + + /** + * Tell whether the specified service class is available. + * Iff getService() could return a non-null value for the + * specified service, this method will return <code>true</code>. + * + * @param serviceClass the service class to check on. + * @return whether the specified service class is available. + */ + boolean hasService (Class serviceClass); + + /** + * Add a listener on all adds and removes of services. + * @param listener the listener to add. + */ + void addBeanContextServicesListener (BeanContextServicesListener listener); + + /** + * Remove a listener on all adds and removes of services. + * @specnote it is not certain whether this should remove this + * listener if it was specified in + * <code>getService()</code>. + * @param listener the listener to add. + */ + void removeBeanContextServicesListener (BeanContextServicesListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java b/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java new file mode 100644 index 000000000..2081080dd --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java @@ -0,0 +1,56 @@ +/* java.beans.beancontext.BeanContextServicesListener + Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Listens for service add and revoke events. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServicesListener extends BeanContextServiceRevokedListener { + /** + * Called by <code>BeanContextServices</code> whenever a service is made available. + * + * @param event the service revoked event, with useful information + * about the new service. + */ + void serviceAvailable(BeanContextServiceAvailableEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java new file mode 100644 index 000000000..30b4a6fbd --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java @@ -0,0 +1,896 @@ +/* BeanContextServicesSupport.java -- + Copyright (C) 2003, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.TooManyListenersException; + +/** + * This is a helper class for implementing a bean context which + * supplies services. It is intended to be used either by + * subclassing or by calling methods of this implementation + * from another. + * + * @author Michael Koch + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.2 + */ +public class BeanContextServicesSupport + extends BeanContextSupport + implements BeanContextServices +{ + private static final long serialVersionUID = -8494482757288719206L; + + protected class BCSSChild + extends BeanContextSupport.BCSChild + { + private static final long serialVersionUID = -3263851306889194873L; + + BCSSChild(Object targetChild, Object peer) + { + super(targetChild, peer); + } + } + + protected class BCSSProxyServiceProvider + implements BeanContextServiceProvider, + BeanContextServiceRevokedListener + { + private static final long serialVersionUID = 7078212910685744490L; + + private BeanContextServiceProvider provider; + + BCSSProxyServiceProvider(BeanContextServiceProvider p) + { + provider = p; + } + + public Iterator getCurrentServiceSelectors (BeanContextServices bcs, + Class serviceClass) + { + return provider.getCurrentServiceSelectors(bcs, serviceClass); + } + + public Object getService (BeanContextServices bcs, + Object requestor, + Class serviceClass, + Object serviceSelector) + { + return provider.getService(bcs, requestor, serviceClass, + serviceSelector); + } + + public void releaseService (BeanContextServices bcs, + Object requestor, + Object service) + { + provider.releaseService(bcs, requestor, service); + } + + public void serviceRevoked (BeanContextServiceRevokedEvent bcsre) + { + if (provider instanceof BeanContextServiceRevokedListener) + ((BeanContextServiceRevokedListener) provider).serviceRevoked(bcsre); + } + } + + protected static class BCSSServiceProvider + implements Serializable + { + private static final long serialVersionUID = 861278251667444782L; + + protected BeanContextServiceProvider serviceProvider; + + private Class serviceClass; + + private BCSSServiceProvider(Class serviceClass, + BeanContextServiceProvider provider) + { + this.serviceClass = serviceClass; + serviceProvider = provider; + } + + protected BeanContextServiceProvider getServiceProvider() + { + return serviceProvider; + } + + private Class getServiceClass() + { + return serviceClass; + } + + } + + /** + * Represents a request for a service. This is + * a common superclass used by the classes which maintain + * the listener-requestor and service-requestor relationships. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static abstract class Request + { + private Object requestor; + + public Request(Object requestor) + { + this.requestor = requestor; + } + + public boolean equals(Object obj) + { + if (obj instanceof Request) + { + Request req = (Request) obj; + return req.getRequestor().equals(requestor); + } + return false; + } + + public Object getRequestor() + { + return requestor; + } + + } + + /** + * Represents a relationship between a service requestor + * and a revocation listener. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static class ServiceRequest + extends Request + { + + private BeanContextServiceRevokedListener listener; + + public ServiceRequest(Object requestor, + BeanContextServiceRevokedListener listener) + { + super(requestor); + this.listener = listener; + } + + public boolean equals(Object obj) + { + if (obj instanceof ServiceRequest) + { + ServiceRequest sr = (ServiceRequest) obj; + return (super.equals(obj) && + sr.getListener().equals(listener)); + } + return false; + } + + public BeanContextServiceRevokedListener getListener() + { + return listener; + } + } + + /** + * Represents a relationship between a service requestor + * and a service instance. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static class ServiceLease + extends Request + { + + private Object service; + + public ServiceLease(Object requestor, Object service) + { + super(requestor); + this.service = service; + } + + public boolean equals(Object obj) + { + if (obj instanceof ServiceLease) + { + ServiceLease sl = (ServiceLease) obj; + return (super.equals(obj) && + sl.getService().equals(service)); + } + return false; + } + + public Object getService() + { + return service; + } + } + + /** + * A collection of listeners who receive availability + * and revocation notifications. + */ + protected transient ArrayList bcsListeners; + + protected transient BCSSProxyServiceProvider proxy; + + /** + * The number of serializable service providers. + */ + protected transient int serializable; + + /** + * A map of registered services, linking the service + * class to its associated {@link BCSSServiceProvider}. + */ + protected transient HashMap services; + + /** + * A map of children to a list of services they + * have obtained. + */ + private transient HashMap serviceUsers; + + /** + * A map of services to {@link ServiceRequest}s. + */ + private transient HashMap serviceRequests; + + /** + * A map of {@link ServiceLease}s to providers. + */ + private transient HashMap serviceLeases; + + /** + * Construct a {@link BeanContextServicesSupport} instance. + */ + public BeanContextServicesSupport () + { + super(); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context services peer (<code>null</code> permitted). + */ + public BeanContextServicesSupport (BeanContextServices peer) + { + super(peer); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context peer (<code>null</code> permitted). + * @param locale the locale (<code>null</code> permitted, equivalent to + * the default locale). + */ + public BeanContextServicesSupport(BeanContextServices peer, Locale locale) + { + super(peer, locale); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context peer (<code>null</code> permitted). + * @param locale the locale (<code>null</code> permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + */ + public BeanContextServicesSupport(BeanContextServices peer, Locale locale, + boolean dtime) + { + super(peer, locale, dtime); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context peer (<code>null</code> permitted). + * @param locale the locale (<code>null</code> permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + * @param visible initial value of the <code>okToUseGui</code> flag. + */ + public BeanContextServicesSupport(BeanContextServices peer, Locale locale, + boolean dtime, boolean visible) + { + super(peer, locale, dtime, visible); + } + + /** + * Adds a new listener for service availability and + * revocation events. + * + * @param listener the listener to add. + */ + public void addBeanContextServicesListener + (BeanContextServicesListener listener) + { + synchronized (bcsListeners) + { + if (! bcsListeners.contains(listener)) + bcsListeners.add(listener); + } + } + + /** + * Registers a new service from the specified service provider. + * The service is internally associated with the service provider + * and a <code>BeanContextServiceAvailableEvent</code> is fired. If + * the service is already registered, then this method instead + * returns <code>false</code>. This is equivalent to calling + * <code>addService(serviceClass, bcsp, true)</code>. + * + * @param serviceClass the class of the service to be registered. + * @param bcsp the provider of the given service. + * @return true if the service was registered successfully. + * @see #addService(Class, BeanContextServiceProvider, boolean) + */ + public boolean addService (Class serviceClass, + BeanContextServiceProvider bcsp) + { + return addService(serviceClass, bcsp, true); + } + + /** + * Registers a new service from the specified service provider. + * The service is internally associated with the service provider + * and (if <code>fireEvent</code> is true) a + * <code>BeanContextServiceAvailableEvent</code> is fired. If + * the service is already registered, then this method instead + * returns <code>false</code>. + * + * @param serviceClass the class of the service to be registered. + * @param bcsp the provider of the given service. + * @param fireEvent true if a service availability event should + * be fired. + * @return true if the service was registered successfully. + */ + protected boolean addService (Class serviceClass, + BeanContextServiceProvider bcsp, + boolean fireEvent) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + if (services.containsKey(serviceClass)) + return false; + services.put(serviceClass, + createBCSSServiceProvider(serviceClass, bcsp)); + if (bcsp instanceof Serializable) + ++serializable; + if (fireEvent) + fireServiceAdded(serviceClass); + return true; + } + } + } + + /** + * Deserializes any service providers which are serializable. This + * method is called by the <code>readObject</code> method of + * {@link BeanContextSupport} prior to deserialization of the children. + * Subclasses may envelope its behaviour in order to read further + * serialized data to the stream. + * + * @param ois the stream from which data is being deserialized. + * @throws IOException if an I/O error occurs. + * @throws ClassNotFoundException if the class of a deserialized object + * can not be found. + */ + protected void bcsPreDeserializationHook (ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + serializable = ois.readInt(); + for (int a = 0; a < serializable; ++a) + { + BCSSServiceProvider bcsssp = (BCSSServiceProvider) ois.readObject(); + addService(bcsssp.getServiceClass(), bcsssp.getServiceProvider()); + } + } + + /** + * Serializes any service providers which are serializable. This + * method is called by the <code>writeObject</code> method of + * {@link BeanContextSupport} prior to serialization of the children. + * Subclasses may envelope its behaviour in order to add further + * serialized data to the stream. + * + * @param oos the stream to which data is being serialized. + * @throws IOException if an I/O error occurs. + */ + protected void bcsPreSerializationHook (ObjectOutputStream oos) + throws IOException + { + oos.writeInt(serializable); + synchronized (services) + { + Iterator i = services.values().iterator(); + while (i.hasNext()) + { + BCSSServiceProvider bcsssp = (BCSSServiceProvider) i.next(); + if (bcsssp.getServiceProvider() instanceof Serializable) + oos.writeObject(bcsssp); + } + } + } + + /** + * Revokes any services used by a child that has just been removed. + * The superclass ({@link BeanContextSupport}) calls this method + * when a child has just been successfully removed. Subclasses can + * extend this method in order to perform additional operations + * on child removal. + * + * @param child the child being removed. + * @param bcsc the support object for the child. + */ + protected void childJustRemovedHook (Object child, + BeanContextSupport.BCSChild bcsc) + { + if (child instanceof BeanContextChild) + { + BeanContextChild bcchild = (BeanContextChild) child; + Iterator childServices = ((List) serviceUsers.get(bcchild)).iterator(); + while (childServices.hasNext()) + releaseService(bcchild, this, childServices.next()); + serviceUsers.remove(bcchild); + } + } + + /** + * Overrides the {@link BeanContextSupport#createBCSChild} method + * so as to use a {@link BCSSChild} instead. + * + * @param targetChild the child to create the child for. + * @param peer the peer which relates to the child if a proxy is used. + * @return a new instance of {@link BCSSChild}. + */ + protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, + Object peer) + { + return new BCSSChild(targetChild, peer); + } + + /** + * Provides a hook so that subclasses can replace the + * {@link BCSSServiceProvider} class, used to store registered + * service providers, with a subclass without replacing the + * {@link #addService(Class, BeanContextServiceProvider)} method. + * + * @param sc the class of service being registered. + * @param bcsp the provider of the service. + * @return a instance of {@link BCSSServiceProvider} wrapping the provider. + */ + protected BeanContextServicesSupport.BCSSServiceProvider + createBCSSServiceProvider (Class sc, BeanContextServiceProvider bcsp) + { + return new BCSSServiceProvider(sc, bcsp); + } + + /** + * Sends a <code>BeanContextServiceAvailableEvent</code> to all + * registered listeners. + * + * @param bcssae the event to send. + */ + protected final void fireServiceAdded (BeanContextServiceAvailableEvent bcssae) + { + synchronized (bcsListeners) + { + int size = bcsListeners.size(); + for (int i = 0; i < size; ++i) + { + BeanContextServicesListener bcsl + = (BeanContextServicesListener) bcsListeners.get(i); + bcsl.serviceAvailable(bcssae); + } + } + } + + /** + * Sends a <code>BeanContextServiceAvailableEvent</code> to all + * registered listeners. + * + * @param serviceClass the service that is now available. + * @see #fireServiceAdded(BeanContextServiceAvailableEvent) + */ + protected final void fireServiceAdded (Class serviceClass) + { + fireServiceAdded(new BeanContextServiceAvailableEvent(this, + serviceClass)); + } + + /** + * Sends a <code>BeanContextServiceRevokedEvent</code> to all + * registered listeners. + * + * @param event the event to send. + */ + protected final void fireServiceRevoked(BeanContextServiceRevokedEvent event) + { + synchronized (bcsListeners) + { + int size = bcsListeners.size(); + for (int i = 0; i < size; ++i) + { + BeanContextServicesListener bcsl + = (BeanContextServicesListener) bcsListeners.get(i); + bcsl.serviceRevoked(event); + } + List requests = (List) serviceRequests.get(event.getServiceClass()); + if (requests != null) + { + Iterator i = requests.iterator(); + while (i.hasNext()) + { + ServiceRequest r = (ServiceRequest) i.next(); + r.getListener().serviceRevoked(event); + } + } + } + } + + /** + * Sends a <code>BeanContextServiceRevokedEvent</code> to all + * registered listeners. + * + * @param serviceClass the service that has been revoked. + * @see #fireServiceRevoked(BeanContextServiceRevokedEvent) + */ + protected final void fireServiceRevoked (Class serviceClass, + boolean revokeNow) + { + fireServiceRevoked(new BeanContextServiceRevokedEvent(this, serviceClass, + revokeNow)); + } + + /** + * Returns the services peer given at construction time, + * or <code>null</code> if no peer was given. + * + * @return the {@link BeanContextServices} peer. + */ + public BeanContextServices getBeanContextServicesPeer () + { + return (BeanContextServices) beanContextChildPeer; + } + + /** + * Returns <code>child</code> as an instance of + * {@link BeanContextServicesListener}, or <code>null</code> if + * <code>child</code> does not implement that interface. + * + * @param child the child (<code>null</code> permitted). + * + * @return The child cast to {@link BeanContextServicesListener}. + */ + protected static final BeanContextServicesListener + getChildBeanContextServicesListener(Object child) + { + if (child instanceof BeanContextServicesListener) + return (BeanContextServicesListener) child; + else + return null; + } + + /** + * Returns an iterator over the currently available + * services. + * + * @return an iterator over the currently available services. + */ + public Iterator getCurrentServiceClasses () + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + return services.keySet().iterator(); + } + } + } + + /** + * Returns an iterator over the service selectors of the service + * provider for the given service. The iterator is actually + * obtained by calling the + * {@link BeanContextServiceProvider#getCurrentServiceSelectors} + * of the provider itself. If the specified service is not available, + * <code>null</code> is returned. + * + * @param serviceClass the service whose provider's selectors should + * be iterated over. + * @return an {@link Iterator} over the service selectors of the + * provider of the given service. + */ + public Iterator getCurrentServiceSelectors (Class serviceClass) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + BeanContextServiceProvider bcsp + = ((BCSSServiceProvider) + services.get(serviceClass)).getServiceProvider(); + if (bcsp == null) + return null; + else + return bcsp.getCurrentServiceSelectors(this, serviceClass); + } + } + } + + /** + * Retrieves the specified service. If a provider for the service + * is registered in this context, then the request is passed on to + * the provider and the service returned. Otherwise, the request + * is delegated to a parent {@link BeanContextServices}, if possible. + * If the service can not be found at all, then <code>null</code> + * is returned. + * + * @param child the child obtaining the reference. + * @param requestor the requestor of the service, which may be the + * child itself. + * @param serviceClass the service being requested. + * @param serviceSelector an additional service-dependent parameter + * (may be <code>null</code> if not appropriate). + * @param bcsrl a listener used to notify the requestor that the service + * has since been revoked. + * @return a reference to the service requested, or <code>null</code>. + * @throws TooManyListenersException according to Sun's documentation. + */ + public Object getService (BeanContextChild child, Object requestor, + Class serviceClass, Object serviceSelector, + BeanContextServiceRevokedListener bcsrl) + throws TooManyListenersException + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + Object service; + BeanContextServiceProvider provider = ((BCSSServiceProvider) + services.get(serviceClass)).getServiceProvider(); + if (provider != null) + { + service = provider.getService(this, requestor, serviceClass, + serviceSelector); + List childServices = (List) serviceUsers.get(child); + if (childServices == null) + { + childServices = new ArrayList(); + serviceUsers.put(child, childServices); + } + childServices.add(serviceClass); + } + else + { + BeanContextServices peer = getBeanContextServicesPeer(); + if (peer != null) + service = peer.getService(child, requestor, serviceClass, + serviceSelector, bcsrl); + else + service = null; + } + if (service != null) + { + ServiceRequest request = new ServiceRequest(requestor, bcsrl); + Set requests = (Set) serviceRequests.get(serviceClass); + if (requests == null) + { + requests = new HashSet(); + serviceRequests.put(serviceClass, requests); + } + requests.add(request); + ServiceLease lease = new ServiceLease(requestor, service); + serviceLeases.put(lease, provider); + } + return service; + } + } + } + + /** + * Returns true if the specified service is available. + * + * @param serviceClass the service to check for. + * @return true if the service is available. + */ + public boolean hasService (Class serviceClass) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + return services.containsKey(serviceClass); + } + } + } + + public void initialize () + { + super.initialize(); + + bcsListeners = new ArrayList(); + services = new HashMap(); + serviceUsers = new HashMap(); + serviceRequests = new HashMap(); + serviceLeases = new HashMap(); + } + + /** + * Subclasses may override this method to allocate resources + * from the nesting bean context. + */ + protected void initializeBeanContextResources() + { + /* Purposefully left empty */ + } + + /** + * Relinquishes any resources obtained from the parent context. + * Specifically, those services obtained from the parent are revoked. + * Subclasses may override this method to deallocate resources + * from the nesting bean context. + */ + protected void releaseBeanContextResources() + { + /* Purposefully left empty */ + } + + /** + * Releases the reference to a service held by a + * {@link BeanContextChild} (or an arbitrary object associated + * with it). It simply calls the appropriate method on the + * underlying provider. + * + * @param child the child who holds the reference. + * @param requestor the object that requested the reference. + * @param service the service being released. + */ + public void releaseService (BeanContextChild child, Object requestor, + Object service) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + ServiceLease lease = new ServiceLease(requestor, service); + BeanContextServiceProvider provider = (BeanContextServiceProvider) + serviceLeases.get(lease); + if (provider != null) + provider.releaseService(this, requestor, service); + else + { + BeanContextServices peer = getBeanContextServicesPeer(); + if (peer != null) + peer.releaseService(child, requestor, service); + } + serviceLeases.remove(lease); + } + } + } + + public void removeBeanContextServicesListener + (BeanContextServicesListener listener) + { + synchronized (bcsListeners) + { + bcsListeners.remove(listener); + } + } + + /** + * Revokes the given service. A {@link BeanContextServiceRevokedEvent} is + * emitted to all registered {@link BeanContextServiceRevokedListener}s + * and {@link BeanContextServiceListener}s. If <code>revokeCurrentServicesNow</code> + * is true, termination of the service is immediate. Otherwise, prior + * acquisitions of the service by requestors remain valid. + * + * @param serviceClass the service to revoke. + * @param bcsp the provider of the revoked service. + * @param revokeCurrentServicesNow true if this is an exceptional circumstance + * where service should be immediately revoked. + */ + public void revokeService (Class serviceClass, BeanContextServiceProvider bcsp, + boolean revokeCurrentServicesNow) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + fireServiceRevoked(serviceClass, revokeCurrentServicesNow); + services.remove(serviceClass); + if (bcsp instanceof Serializable) + --serializable; + } + } + } + + public void serviceAvailable (BeanContextServiceAvailableEvent bcssae) + { + synchronized (services) + { + Class klass = bcssae.getServiceClass(); + if (services.containsKey(klass)) + return; + Iterator it = bcsChildren(); + while (it.hasNext()) + { + Object obj = it.next(); + if (obj instanceof BeanContextServices) + ((BeanContextServices) obj).serviceAvailable(bcssae); + } + } + } + + public void serviceRevoked (BeanContextServiceRevokedEvent bcssre) + { + synchronized (services) + { + Class klass = bcssre.getServiceClass(); + if (services.containsKey(klass)) + return; + Iterator it = bcsChildren(); + while (it.hasNext()) + { + Object obj = it.next(); + if (obj instanceof BeanContextServices) + ((BeanContextServices) obj).serviceRevoked(bcssre); + } + } + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java new file mode 100644 index 000000000..fdae387d4 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java @@ -0,0 +1,1079 @@ +/* BeanContextSupport.java -- + Copyright (C) 2003, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.Beans; +import java.beans.DesignMode; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.Visibility; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +/** + * This is a helper class for implementing a bean context. It is + * intended to be used either by subclassing or by calling methods + * of this implementation from another. + * + * @author Michael Koch + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.2 + */ +public class BeanContextSupport extends BeanContextChildSupport + implements BeanContext, Serializable, PropertyChangeListener, + VetoableChangeListener +{ + private static final long serialVersionUID = -4879613978649577204L; + + /** + * Deserializes a stored bean context. Hook methods are provided to allow + * subclasses to perform their own deserialization after the default + * deserialization but prior to the deserialization of the children. Note that + * {@link #readChildren(ObjectInputStream)} is only called if there + * is no distinct peer. If there is, the peer is expected to call + * the method instead. + * + * @param s the stream to deserialize. + * @throws ClassNotFoundException if the class of an object being deserialized + * could not be found. + * @throws IOException if an I/O error occurs. + */ + private void readObject (ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + bcsPreDeserializationHook(s); + BeanContext peer = getBeanContextPeer(); + if (peer == null || peer == this) + readChildren(s); + } + + /** + * Serializes a bean context. Hook methods are provided to allow + * subclasses to perform their own serialization after the default + * serialization but prior to serialization of the children. Note that + * {@link #writeChildren(ObjectOutputStream)} is only called if there + * is no distinct peer. If there is, the peer is expected to call + * the method instead. + * + * @param s the stream to serialize. + * @throws ClassNotFoundException if the class of an object being deserialized + * could not be found. + * @throws IOException if an I/O error occurs. + */ + private void writeObject (ObjectOutputStream s) + throws ClassNotFoundException, IOException + { + serializing = true; + s.defaultWriteObject(); + bcsPreSerializationHook(s); + BeanContext peer = getBeanContextPeer(); + if (peer == null || peer == this) + writeChildren(s); + serializing = false; + } + + protected class BCSChild implements Serializable + { + private static final long serialVersionUID = -5815286101609939109L; + + private Object targetChild; + private Object peer; + + BCSChild(Object targetChild, Object peer) + { + this.targetChild = targetChild; + this.peer = peer; + } + + private Object getTargetChild() + { + return targetChild; + } + + } + + protected static final class BCSIterator implements Iterator + { + private Iterator child; + + BCSIterator(Iterator child) + { + this.child = child; + } + + public boolean hasNext () + { + return child.hasNext(); + } + + public Object next () + { + return child.next(); + } + + public void remove () + { + // This must be a noop remove operation. + } + } + + protected transient ArrayList bcmListeners; + + protected transient HashMap children; + + protected transient boolean designTime; + + protected transient Locale locale; + + protected transient boolean okToUseGui; + + private transient boolean serializing; + + /** + * Construct a BeanContextSupport instance. + */ + public BeanContextSupport () + { + this (null, null, false, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (<code>null</code> permitted). + */ + public BeanContextSupport(BeanContext peer) + { + this (peer, null, false, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (<code>null</code> permitted). + * @param locale the locale (<code>null</code> permitted, equivalent to + * the default locale). + */ + public BeanContextSupport (BeanContext peer, Locale locale) + { + this (peer, locale, false, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (<code>null</code> permitted). + * @param locale the locale (<code>null</code> permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + */ + public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime) + { + this (peer, locale, dtime, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (<code>null</code> permitted). + * @param locale the locale (<code>null</code> permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + * @param visible initial value of the <code>okToUseGui</code> flag. + */ + public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime, + boolean visible) + { + super(peer); + + this.locale = locale == null ? Locale.getDefault() : locale; + designTime = dtime; + okToUseGui = visible; + + initialize (); + } + + /** + * <p> + * Add a child to the bean context. A child can be a simple + * <code>Object</code>, a <code>BeanContextChild</code> + * or another <code>BeanContext</code>. + * </p> + * <p> + * The children of a <code>BeanContext</code> form a set. As + * a result, this method returns <code>false</code> if the given + * object is already a child of this context. + * </p> + * <p> + * If the child is a <code>BeanContextChild</code>, or a proxy + * for such a child, the <code>setBeanContext()</code> method + * is invoked on the child. If this operation is vetoed by the + * child, via throwing a <code>PropertyVetoException</code>, + * then the current completion state of the <code>add()</code> + * operation is rolled back and a <code>IllegalStateException</code> + * is thrown. If the <code>BeanContextChild</code> is successfully + * added, then the context registers with its + * <code>PropertyChangeListener</code> and + * <code>VetoableChangeListener</code> for "beanContext" events. + * </p> + * <p> + * If the child implements <code>java.beans.Visibility</code>, + * then its ability to use a GUI is set based on that of + * this context. + * </p> + * <p> + * A <code>BeanContextMembershipEvent</code> is fired when the + * child is successfully added to the bean context. + * </p> + * <p> + * This method is synchronized over the global hierarchy lock. + * </p> + * + * @param targetChild the child to add. + * @return false if the child has already been added. + * @throws IllegalArgumentException if the child is null. + * @throws IllegalStateException if the child vetos the setting + * of its context. + */ + public boolean add(Object targetChild) + { + synchronized (globalHierarchyLock) + { + if (targetChild == null) + throw new IllegalArgumentException(); + + BCSChild child; + synchronized (children) + { + if (children.containsKey(targetChild) + || ! validatePendingAdd(targetChild)) + return false; + child = createBCSChild(targetChild, beanContextChildPeer); + children.put(targetChild, child); + } + synchronized (targetChild) + { + BeanContextChild bcChild = null; + if (targetChild instanceof BeanContextChild) + bcChild = (BeanContextChild) targetChild; + if (targetChild instanceof BeanContextProxy) + bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); + if (bcChild != null) + try + { + bcChild.setBeanContext(this); + bcChild.addVetoableChangeListener("beanContext", this); + bcChild.addPropertyChangeListener("beanContext", this); + } + catch (PropertyVetoException e) + { + synchronized (children) + { + children.remove(targetChild); + } + throw new IllegalStateException("The child refused to " + + "associate itself with " + + "this context.", e); + } + if (targetChild instanceof Visibility) + { + Visibility visibleChild = (Visibility) targetChild; + if (okToUseGui) + visibleChild.okToUseGui(); + else + visibleChild.dontUseGui(); + } + childJustAddedHook(targetChild, child); + } + fireChildrenAdded(new BeanContextMembershipEvent(this, + new Object[]{ targetChild })); + return true; + } + } + + public boolean addAll (Collection c) + { + // Intentionally throws an exception. + throw new UnsupportedOperationException(); + } + + public void addBeanContextMembershipListener + (BeanContextMembershipListener listener) + { + synchronized (bcmListeners) + { + if (! bcmListeners.contains(listener)) + bcmListeners.add(listener); + } + } + + /** + * Returns true if this bean needs a GUI + * but is being prevented from using one. + * + * @return true if <code>needsGui()</code> + * is true but the bean has been + * told not to use it. + */ + public boolean avoidingGui() + { + return needsGui() && (!okToUseGui); + } + + protected Iterator bcsChildren () + { + synchronized (children) + { + return new BCSIterator(children.values().iterator()); + } + } + + /** + * Subclasses may use this method to perform their own deserialization + * after the default deserialization process has taken place, but + * prior to the deserialization of the children. It should not + * be used to replace the implementation of <code>readObject</code> + * in the subclass. + * + * @param ois the input stream. + * @throws ClassNotFoundException if the class of an object being deserialized + * could not be found. + * @throws IOException if an I/O error occurs. + */ + protected void bcsPreDeserializationHook (ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + /* Purposefully left empty */ + } + + /** + * Subclasses may use this method to perform their own serialization + * after the default serialization process has taken place, but + * prior to the serialization of the children. It should not + * be used to replace the implementation of <code>writeObject</code> + * in the subclass. + * + * @param oos the output stream. + * @throws IOException if an I/O error occurs. + */ + protected void bcsPreSerializationHook (ObjectOutputStream oos) + throws IOException + { + /* Purposefully left empty */ + } + + /** + * Called when a child is deserialized. + * + * @param child the deserialized child. + * @param bcsc the deserialized context wrapper for the child. + */ + protected void childDeserializedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + // Do nothing in the base class. + } + + protected void childJustAddedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + // Do nothing in the base class. + } + + protected void childJustRemovedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + // Do nothing in the base class. + } + + protected static final boolean classEquals (Class first, Class second) + { + // Lame function! + return (first == second || first.getName().equals(second.getName())); + } + + public void clear () + { + // This is the right thing to do. + // The JDK docs are really bad here. + throw new UnsupportedOperationException(); + } + + public boolean contains (Object o) + { + synchronized (children) + { + return children.containsKey(o); + } + } + + public boolean containsAll (Collection c) + { + synchronized (children) + { + Iterator it = c.iterator(); + while (it.hasNext()) + if (! children.containsKey(it.next())) + return false; + } + return true; + } + + public boolean containsKey (Object o) + { + synchronized (children) + { + return children.containsKey(o); + } + } + + protected final Object[] copyChildren () + { + synchronized (children) + { + return children.keySet().toArray(); + } + } + + protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, Object peer) + { + return new BCSChild(targetChild, peer); + } + + /** + * Deserializes objects (written by {@link #serialize(ObjectOutputStream, + * Collection)}) and adds them to the specified collection. + * + * @param ois the input stream (<code>null</code> not permitted). + * @param coll the collection to add the objects to (<code>null</code> not + * permitted). + * + * @throws ClassNotFoundException + * @throws IOException + * + * @see #serialize(ObjectOutputStream, Collection) + */ + protected final void deserialize (ObjectInputStream ois, Collection coll) + throws ClassNotFoundException, IOException + { + int itemCount = ois.readInt(); + for (int i = 0; i < itemCount; i++) + coll.add(ois.readObject()); + } + + /** + * Informs this bean that is should not make + * use of the GUI. + */ + public void dontUseGui() + { + okToUseGui = false; + } + + protected final void fireChildrenAdded (BeanContextMembershipEvent bcme) + { + synchronized (bcmListeners) + { + Iterator it = bcmListeners.iterator(); + while (it.hasNext()) + { + BeanContextMembershipListener l + = (BeanContextMembershipListener) it.next(); + l.childrenAdded(bcme); + } + } + } + + protected final void fireChildrenRemoved (BeanContextMembershipEvent bcme) + { + synchronized (bcmListeners) + { + Iterator it = bcmListeners.iterator(); + while (it.hasNext()) + { + BeanContextMembershipListener l + = (BeanContextMembershipListener) it.next(); + l.childrenRemoved(bcme); + } + } + } + + /** + * Returns the bean context peer. + * + * @return The bean context peer. + * + * @see BeanContextChildSupport#beanContextChildPeer + */ + public BeanContext getBeanContextPeer() + { + return (BeanContext) beanContextChildPeer; + } + + /** + * Returns the {@link BeanContextChild} implementation for the given child. + * + * @param child the child (<code>null</code> permitted). + * + * @return The bean context child. + * + * @throws IllegalArgumentException if <code>child</code> implements both + * the {@link BeanContextChild} and {@link BeanContextProxy} interfaces. + */ + protected static final BeanContextChild getChildBeanContextChild(Object child) + { + if (child == null) + return null; + if (child instanceof BeanContextChild && child instanceof BeanContextProxy) + throw new IllegalArgumentException("Child cannot implement " + + "BeanContextChild and BeanContextProxy simultaneously."); + if (child instanceof BeanContextChild) + return (BeanContextChild) child; + if (child instanceof BeanContextProxy) + return ((BeanContextProxy) child).getBeanContextProxy(); + return null; + } + + /** + * Returns <code>child</code> as an instance of + * {@link BeanContextMembershipListener}, or <code>null</code> if + * <code>child</code> does not implement that interface. + * + * @param child the child (<code>null</code> permitted). + * + * @return The child cast to {@link BeanContextMembershipListener}. + */ + protected static final BeanContextMembershipListener + getChildBeanContextMembershipListener(Object child) + { + if (child instanceof BeanContextMembershipListener) + return (BeanContextMembershipListener) child; + else + return null; + } + + /** + * Returns <code>child</code> as an instance of + * {@link PropertyChangeListener}, or <code>null</code> if <code>child</code> + * does not implement that interface. + * + * @param child the child (<code>null</code> permitted). + * + * @return The child cast to {@link PropertyChangeListener}. + */ + protected static final PropertyChangeListener getChildPropertyChangeListener( + Object child) + { + if (child instanceof PropertyChangeListener) + return (PropertyChangeListener) child; + else + return null; + } + + /** + * Returns <code>child</code> as an instance of {@link Serializable}, or + * <code>null</code> if <code>child</code> does not implement that + * interface. + * + * @param child the child (<code>null</code> permitted). + * + * @return The child cast to {@link Serializable}. + */ + protected static final Serializable getChildSerializable(Object child) + { + if (child instanceof Serializable) + return (Serializable) child; + else + return null; + } + + /** + * Returns <code>child</code> as an instance of + * {@link VetoableChangeListener}, or <code>null</code> if <code>child</code> + * does not implement that interface. + * + * @param child the child (<code>null</code> permitted). + * + * @return The child cast to {@link VetoableChangeListener}. + */ + protected static final VetoableChangeListener getChildVetoableChangeListener( + Object child) + { + if (child instanceof VetoableChangeListener) + return (VetoableChangeListener) child; + else + return null; + } + + /** + * Returns <code>child</code> as an instance of {@link Visibility}, or + * <code>null</code> if <code>child</code> does not implement that interface. + * + * @param child the child (<code>null</code> permitted). + * + * @return The child cast to {@link Visibility}. + */ + protected static final Visibility getChildVisibility(Object child) + { + if (child instanceof Visibility) + return (Visibility) child; + else + return null; + } + + public Locale getLocale () + { + return locale; + } + + public URL getResource (String name, BeanContextChild bcc) + { + if (! contains(bcc)) + throw new IllegalArgumentException("argument not a child"); + ClassLoader loader = bcc.getClass().getClassLoader(); + return (loader == null ? ClassLoader.getSystemResource(name) + : loader.getResource(name)); + } + + public InputStream getResourceAsStream (String name, BeanContextChild bcc) + { + if (! contains(bcc)) + throw new IllegalArgumentException("argument not a child"); + ClassLoader loader = bcc.getClass().getClassLoader(); + return (loader == null ? ClassLoader.getSystemResourceAsStream(name) + : loader.getResourceAsStream(name)); + } + + protected void initialize () + { + bcmListeners = new ArrayList(); + children = new HashMap(); + } + + /** + * This is a convenience method for instantiating a bean inside this + * context. It delegates to the appropriate method in + * <code>java.beans.Beans</code> using the context's classloader. + * + * @param beanName the name of the class of bean to instantiate. + * @throws IOException if an I/O error occurs in loading the class. + * @throws ClassNotFoundException if the class, <code>beanName</code>, + * can not be found. + */ + public Object instantiateChild (String beanName) + throws IOException, ClassNotFoundException + { + return Beans.instantiate(getClass().getClassLoader(), beanName, this); + } + + /** + * Returns <code>true</code> if the <code>BeanContext</code> is in + * design time mode, and <code>false</code> if it is in runtime mode. + * + * @return A boolean. + * + * @see #setDesignTime(boolean) + */ + public boolean isDesignTime() + { + return designTime; + } + + /** + * Returns true if this bean context has no children. + * + * @return true if there are no children. + */ + public boolean isEmpty () + { + synchronized (children) + { + return children.isEmpty(); + } + } + + /** + * Returns true if the bean context is in the process + * of being serialized. + * + * @return true if the context is being serialized. + */ + public boolean isSerializing() + { + return serializing; + } + + public Iterator iterator () + { + synchronized (children) + { + return children.keySet().iterator(); + } + } + + /** + * Returns false as this bean does not a + * GUI for its operation. + * + * @return false + */ + public boolean needsGui() + { + return false; + } + + /** + * Informs this bean that it is okay to make use of + * the GUI. + */ + public void okToUseGui () + { + okToUseGui = true; + } + + /** + * Subclasses may use this method to catch property changes + * arising from the children of this context. At present, + * we just listen for the beans being assigned to a different + * context and remove them from here if such an event occurs. + * + * @param pce the property change event. + */ + public void propertyChange (PropertyChangeEvent pce) + { + if (pce.getNewValue() != this) + remove(pce.getSource(), false); + } + + /** + * Deserializes the children using the + * {@link #deserialize(ObjectInputStream, Collection} method + * and then calls {@link childDeserializedHook(Object, BCSChild)} + * for each child deserialized. + * + * @param ois the input stream. + * @throws IOException if an I/O error occurs. + */ + public final void readChildren (ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + List temp = new ArrayList(); + deserialize(ois, temp); + Iterator i = temp.iterator(); + synchronized (globalHierarchyLock) + { + synchronized (children) + { + while (i.hasNext()) + { + BCSChild bcs = (BCSChild) i.next(); + childDeserializedHook(bcs.getTargetChild(), bcs); + children.put(bcs.getTargetChild(), bcs); + } + } + } + } + + /** + * Remove the specified child from the context. This is + * the same as calling <code>remove(Object,boolean)</code> + * with a request for the <code>setBeanContext()</code> method + * of the child to be called (i.e. the second argument is true). + * + * @param targetChild the child to remove. + */ + public boolean remove (Object targetChild) + { + return remove(targetChild, true); + } + + /** + * <p> + * Removes a child from the bean context. A child can be a simple + * <code>Object</code>, a <code>BeanContextChild</code> + * or another <code>BeanContext</code>. If the given child is not + * a child of this context, this method returns <code>false</code>. + * </p> + * <p> + * If the child is a <code>BeanContextChild</code>, or a proxy + * for such a child, the <code>setBeanContext()</code> method + * is invoked on the child (if specified). If this operation is vetoed + * by the child, via throwing a <code>PropertyVetoException</code>, + * then the current completion state of the <code>remove()</code> + * operation is rolled back and a <code>IllegalStateException</code> + * is thrown. If the <code>BeanContextChild</code> is successfully + * removed, then the context deregisters with its + * <code>PropertyChangeListener</code> and + * <code>VetoableChangeListener</code> for "beanContext" events. + * </p> + * <p> + * A <code>BeanContextMembershipEvent</code> is fired when the + * child is successfully removed from the bean context. + * </p> + * <p> + * This method is synchronized over the global hierarchy lock. + * </p> + * + * @param targetChild the child to remove. + * @param callChildSetBC true if the <code>setBeanContext()</code> + * method of the child should be called. + * @return false if the child doesn't exist. + * @throws IllegalArgumentException if the child is null. + * @throws IllegalStateException if the child vetos the setting + * of its context. + */ + protected boolean remove (Object targetChild, boolean callChildSetBC) + { + synchronized (globalHierarchyLock) + { + if (targetChild == null) + throw new IllegalArgumentException(); + + BCSChild child; + synchronized (children) + { + if (!children.containsKey(targetChild) + || !validatePendingRemove(targetChild)) + return false; + child = (BCSChild) children.remove(targetChild); + } + synchronized (targetChild) + { + BeanContextChild bcChild = null; + if (targetChild instanceof BeanContextChild) + bcChild = (BeanContextChild) targetChild; + if (targetChild instanceof BeanContextProxy) + bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); + if (bcChild != null) + try + { + if (callChildSetBC) + bcChild.setBeanContext(null); + bcChild.removeVetoableChangeListener("beanContext", this); + bcChild.removePropertyChangeListener("beanContext", this); + } + catch (PropertyVetoException e) + { + synchronized (children) + { + children.put(targetChild, child); + } + throw new IllegalStateException("The child refused to " + + "disassociate itself with " + + "this context.", e); + } + childJustRemovedHook(targetChild, child); + } + fireChildrenRemoved(new BeanContextMembershipEvent(this, + new Object[]{ targetChild })); + return true; + } + } + + public boolean removeAll (Collection c) + { + // Intentionally throws an exception. + throw new UnsupportedOperationException(); + } + + public void removeBeanContextMembershipListener (BeanContextMembershipListener bcml) + { + synchronized (bcmListeners) + { + bcmListeners.remove(bcml); + } + } + + public boolean retainAll (Collection c) + { + // Intentionally throws an exception. + throw new UnsupportedOperationException(); + } + + /** + * Writes the items in the collection to the specified output stream. Items + * in the collection that are not instances of {@link Serializable} + * (this includes <code>null</code>) are simply ignored. + * + * @param oos the output stream (<code>null</code> not permitted). + * @param coll the collection (<code>null</code> not permitted). + * + * @throws IOException + * + * @see #deserialize(ObjectInputStream, Collection) + */ + protected final void serialize(ObjectOutputStream oos, Collection coll) + throws IOException + { + Object[] items = coll.toArray(); + int itemCount = 0; + for (int i = 0; i < items.length; i++) + { + if (items[i] instanceof Serializable) + itemCount++; + } + oos.writeInt(itemCount); + for (int i = 0; i < items.length; i++) + { + if (items[i] instanceof Serializable) + oos.writeObject(items[i]); + } + } + + /** + * Sets the flag that indicates whether or not the + * <code>BeanContext</code> is in design mode. If the flag changes + * value, a {@link PropertyChangeEvent} (with the property name 'designMode') + * is sent to registered listeners. Note that the property name used here + * does NOT match the specification in the {@link DesignMode} interface, we + * match the reference implementation instead - see bug parade entry 4295174. + * + * @param dtime the new value for the flag. + * + * @see #isDesignTime() + */ + public void setDesignTime(boolean dtime) + { + boolean save = designTime; + designTime = dtime; + // note that we use the same property name as Sun's implementation, + // even though this is a known bug: see bug parade entry 4295174 + firePropertyChange("designMode", Boolean.valueOf(save), + Boolean.valueOf(dtime)); + } + + public void setLocale (Locale newLocale) + throws PropertyVetoException + { + if (newLocale == null || locale == newLocale) + return; + fireVetoableChange("locale", locale, newLocale); + Locale oldLocale = locale; + locale = newLocale; + firePropertyChange("locale", oldLocale, newLocale); + } + + public int size () + { + synchronized (children) + { + return children.size(); + } + } + + /** + * Returns an array containing the children of this <code>BeanContext</code>. + * + * @return An array containing the children. + */ + public Object[] toArray() + { + synchronized (children) + { + return children.keySet().toArray(); + } + } + + /** + * Populates, then returns, the supplied array with the children of this + * <code>BeanContext</code>. If the array is too short to hold the + * children, a new array is allocated and returned. If the array is too + * long, it is padded with <code>null</code> items at the end. + * + * @param array an array to populate (<code>null</code> not permitted). + */ + public Object[] toArray(Object[] array) + { + synchronized (children) + { + return children.keySet().toArray(array); + } + } + + protected boolean validatePendingAdd (Object targetChild) + { + return true; + } + + protected boolean validatePendingRemove (Object targetChild) + { + return true; + } + + /** + * Subclasses may use this method to veto changes arising + * from the children of this context. + * + * @param pce the vetoable property change event fired. + */ + public void vetoableChange (PropertyChangeEvent pce) + throws PropertyVetoException + { + /* Purposefully left empty */ + } + + /** + * Serializes the children using the + * {@link #serialize(ObjectOutputStream, Collection} method. + * + * @param oos the output stream. + * @throws IOException if an I/O error occurs. + */ + public final void writeChildren (ObjectOutputStream oos) + throws IOException + { + synchronized (children) + { + serialize(oos, children.values()); + } + } + +} diff --git a/libjava/classpath/java/beans/beancontext/package.html b/libjava/classpath/java/beans/beancontext/package.html new file mode 100644 index 000000000..55e3bd723 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.beans.beancontext package. + Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.beans.beancontext</title></head> + +<body> +<p>Containers and execution environments for beans.</p> + +</body> +</html> diff --git a/libjava/classpath/java/beans/package.html b/libjava/classpath/java/beans/package.html new file mode 100644 index 000000000..5ca800037 --- /dev/null +++ b/libjava/classpath/java/beans/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.beans package. + Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.beans</title></head> + +<body> +<p>Listeners and descriptors for managing beans.</p> + +</body> +</html> |