Showing the changes to java/pkg/java/beans (227 of 18352 files). Show all
Unified · Split
Binary file not shown.
| @@ -0,0 +1,91 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2000, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.applet.Applet; | ||
| 29 | |||
| 30 | import java.beans.beancontext.BeanContext; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * <p> | ||
| 34 | * This interface is designed to work in collusion with java.beans.Beans.instantiate. | ||
| 35 | * The interafce is intended to provide mechanism to allow the proper | ||
| 36 | * initialization of JavaBeans that are also Applets, during their | ||
| 37 | * instantiation by java.beans.Beans.instantiate(). | ||
| 38 | * </p> | ||
| 39 | * | ||
| 40 | * @see java.beans.Beans#instantiate | ||
| 41 | * | ||
| 42 | * @since 1.2 | ||
| 43 | * | ||
| 44 | */ | ||
| 45 | |||
| 46 | |||
| 47 | public interface AppletInitializer { | ||
| 48 | |||
| 49 | /** | ||
| 50 | * <p> | ||
| 51 | * If passed to the appropriate variant of java.beans.Beans.instantiate | ||
| 52 | * this method will be called in order to associate the newly instantiated | ||
| 53 | * Applet (JavaBean) with its AppletContext, AppletStub, and Container. | ||
| 54 | * </p> | ||
| 55 | * <p> | ||
| 56 | * Conformant implementations shall: | ||
| 57 | * <ol> | ||
| 58 | * <li> Associate the newly instantiated Applet with the appropriate | ||
| 59 | * AppletContext. | ||
| 60 | * | ||
| 61 | * <li> Instantiate an AppletStub() and associate that AppletStub with | ||
| 62 | * the Applet via an invocation of setStub(). | ||
| 63 | * | ||
| 64 | * <li> If BeanContext parameter is null, then it shall associate the | ||
| 65 | * Applet with its appropriate Container by adding that Applet to its | ||
| 66 | * Container via an invocation of add(). If the BeanContext parameter is | ||
| 67 | * non-null, then it is the responsibility of the BeanContext to associate | ||
| 68 | * the Applet with its Container during the subsequent invocation of its | ||
| 69 | * addChildren() method. | ||
| 70 | * </ol> | ||
| 71 | * </p> | ||
| 72 | * | ||
| 73 | * @param newAppletBean The newly instantiated JavaBean | ||
| 74 | * @param bCtxt The BeanContext intended for this Applet, or | ||
| 75 | * null. | ||
| 76 | */ | ||
| 77 | |||
| 78 | void initialize(Applet newAppletBean, BeanContext bCtxt); | ||
| 79 | |||
| 80 | /** | ||
| 81 | * <p> | ||
| 82 | * Activate, and/or mark Applet active. Implementors of this interface | ||
| 83 | * shall mark this Applet as active, and optionally invoke its start() | ||
| 84 | * method. | ||
| 85 | * </p> | ||
| 86 | * | ||
| 87 | * @param newApplet The newly instantiated JavaBean | ||
| 88 | */ | ||
| 89 | |||
| 90 | void activate(Applet newApplet); | ||
| 91 | } | ||
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,109 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.lang.ref.Reference; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A BeanDescriptor provides global information about a "bean", | ||
| 32 | * including its Java class, its displayName, etc. | ||
| 33 | * <p> | ||
| 34 | * This is one of the kinds of descriptor returned by a BeanInfo object, | ||
| 35 | * which also returns descriptors for properties, method, and events. | ||
| 36 | */ | ||
| 37 | |||
| 38 | public class BeanDescriptor extends FeatureDescriptor { | ||
| 39 | |||
| 40 | private Reference<Class> beanClassRef; | ||
| 41 | private Reference<Class> customizerClassRef; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Create a BeanDescriptor for a bean that doesn't have a customizer. | ||
| 45 | * | ||
| 46 | * @param beanClass The Class object of the Java class that implements | ||
| 47 | * the bean. For example sun.beans.OurButton.class. | ||
| 48 | */ | ||
| 49 | public BeanDescriptor(Class<?> beanClass) { | ||
| 50 | this(beanClass, null); | ||
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Create a BeanDescriptor for a bean that has a customizer. | ||
| 55 | * | ||
| 56 | * @param beanClass The Class object of the Java class that implements | ||
| 57 | * the bean. For example sun.beans.OurButton.class. | ||
| 58 | * @param customizerClass The Class object of the Java class that implements | ||
| 59 | * the bean's Customizer. For example sun.beans.OurButtonCustomizer.class. | ||
| 60 | */ | ||
| 61 | public BeanDescriptor(Class<?> beanClass, Class<?> customizerClass) { | ||
| 62 | this.beanClassRef = getWeakReference((Class)beanClass); | ||
| 63 | this.customizerClassRef = getWeakReference((Class)customizerClass); | ||
| 64 | |||
| 65 | String name = beanClass.getName(); | ||
| 66 | while (name.indexOf('.') >= 0) { | ||
| 67 | name = name.substring(name.indexOf('.')+1); | ||
| 68 | } | ||
| 69 | setName(name); | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Gets the bean's Class object. | ||
| 74 | * | ||
| 75 | * @return The Class object for the bean. | ||
| 76 | */ | ||
| 77 | public Class<?> getBeanClass() { | ||
| 78 | return (this.beanClassRef != null) | ||
| 79 | ? this.beanClassRef.get() | ||
| 80 | : null; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Gets the Class object for the bean's customizer. | ||
| 85 | * | ||
| 86 | * @return The Class object for the bean's customizer. This may | ||
| 87 | * be null if the bean doesn't have a customizer. | ||
| 88 | */ | ||
| 89 | public Class<?> getCustomizerClass() { | ||
| 90 | return (this.customizerClassRef != null) | ||
| 91 | ? this.customizerClassRef.get() | ||
| 92 | : null; | ||
| 93 | } | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Package-private dup constructor | ||
| 97 | * This must isolate the new object from any changes to the old object. | ||
| 98 | */ | ||
| 99 | BeanDescriptor(BeanDescriptor old) { | ||
| 100 | super(old); | ||
| 101 | beanClassRef = old.beanClassRef; | ||
| 102 | customizerClassRef = old.customizerClassRef; | ||
| 103 | } | ||
| 104 | |||
| 105 | void appendTo(StringBuilder sb) { | ||
| 106 | appendTo(sb, "beanClass", this.beanClassRef); | ||
| 107 | appendTo(sb, "customizerClass", this.customizerClassRef); | ||
| 108 | } | ||
| 109 | } | ||
Binary file not shown.
| @@ -0,0 +1,176 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A bean implementor who wishes to provide explicit information about | ||
| 30 | * their bean may provide a BeanInfo class that implements this BeanInfo | ||
| 31 | * interface and provides explicit information about the methods, | ||
| 32 | * properties, events, etc, of their bean. | ||
| 33 | * <p> | ||
| 34 | * A bean implementor doesn't need to provide a complete set of | ||
| 35 | * explicit information. You can pick and choose which information | ||
| 36 | * you want to provide and the rest will be obtained by automatic | ||
| 37 | * analysis using low-level reflection of the bean classes' methods | ||
| 38 | * and applying standard design patterns. | ||
| 39 | * <p> | ||
| 40 | * You get the opportunity to provide lots and lots of different | ||
| 41 | * information as part of the various XyZDescriptor classes. But | ||
| 42 | * don't panic, you only really need to provide the minimal core | ||
| 43 | * information required by the various constructors. | ||
| 44 | * <P> | ||
| 45 | * See also the SimpleBeanInfo class which provides a convenient | ||
| 46 | * "noop" base class for BeanInfo classes, which you can override | ||
| 47 | * for those specific places where you want to return explicit info. | ||
| 48 | * <P> | ||
| 49 | * To learn about all the behaviour of a bean see the Introspector class. | ||
| 50 | */ | ||
| 51 | |||
| 52 | public interface BeanInfo { | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Gets the beans <code>BeanDescriptor</code>. | ||
| 56 | * | ||
| 57 | * @return A BeanDescriptor providing overall information about | ||
| 58 | * the bean, such as its displayName, its customizer, etc. May | ||
| 59 | * return null if the information should be obtained by automatic | ||
| 60 | * analysis. | ||
| 61 | */ | ||
| 62 | BeanDescriptor getBeanDescriptor(); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Gets the beans <code>EventSetDescriptor</code>s. | ||
| 66 | * | ||
| 67 | * @return An array of EventSetDescriptors describing the kinds of | ||
| 68 | * events fired by this bean. May return null if the information | ||
| 69 | * should be obtained by automatic analysis. | ||
| 70 | */ | ||
| 71 | EventSetDescriptor[] getEventSetDescriptors(); | ||
| 72 | |||
| 73 | /** | ||
| 74 | * A bean may have a "default" event that is the event that will | ||
| 75 | * mostly commonly be used by humans when using the bean. | ||
| 76 | * @return Index of default event in the EventSetDescriptor array | ||
| 77 | * returned by getEventSetDescriptors. | ||
| 78 | * <P> Returns -1 if there is no default event. | ||
| 79 | */ | ||
| 80 | int getDefaultEventIndex(); | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Returns descriptors for all properties of the bean. | ||
| 84 | * May return {@code null} if the information | ||
| 85 | * should be obtained by automatic analysis. | ||
| 86 | * <p> | ||
| 87 | * If a property is indexed, then its entry in the result array | ||
| 88 | * will belong to the {@link IndexedPropertyDescriptor} subclass | ||
| 89 | * of the {@link PropertyDescriptor} class. | ||
| 90 | * A client of the {@code getPropertyDescriptors} method | ||
| 91 | * can use "{@code instanceof}" to check | ||
| 92 | * whether a given {@code PropertyDescriptor} | ||
| 93 | * is an {@code IndexedPropertyDescriptor}. | ||
| 94 | * | ||
| 95 | * @return an array of {@code PropertyDescriptor}s | ||
| 96 | * describing all properties supported by the bean | ||
| 97 | * or {@code null} | ||
| 98 | */ | ||
| 99 | PropertyDescriptor[] getPropertyDescriptors(); | ||
| 100 | |||
| 101 | /** | ||
| 102 | * A bean may have a "default" property that is the property that will | ||
| 103 | * mostly commonly be initially chosen for update by human's who are | ||
| 104 | * customizing the bean. | ||
| 105 | * @return Index of default property in the PropertyDescriptor array | ||
| 106 | * returned by getPropertyDescriptors. | ||
| 107 | * <P> Returns -1 if there is no default property. | ||
| 108 | */ | ||
| 109 | int getDefaultPropertyIndex(); | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Gets the beans <code>MethodDescriptor</code>s. | ||
| 113 | * | ||
| 114 | * @return An array of MethodDescriptors describing the externally | ||
| 115 | * visible methods supported by this bean. May return null if | ||
| 116 | * the information should be obtained by automatic analysis. | ||
| 117 | */ | ||
| 118 | MethodDescriptor[] getMethodDescriptors(); | ||
| 119 | |||
| 120 | /** | ||
| 121 | * This method allows a BeanInfo object to return an arbitrary collection | ||
| 122 | * of other BeanInfo objects that provide additional information on the | ||
| 123 | * current bean. | ||
| 124 | * <P> | ||
| 125 | * If there are conflicts or overlaps between the information provided | ||
| 126 | * by different BeanInfo objects, then the current BeanInfo takes precedence | ||
| 127 | * over the getAdditionalBeanInfo objects, and later elements in the array | ||
| 128 | * take precedence over earlier ones. | ||
| 129 | * | ||
| 130 | * @return an array of BeanInfo objects. May return null. | ||
| 131 | */ | ||
| 132 | BeanInfo[] getAdditionalBeanInfo(); | ||
| 133 | |||
| 134 | /** | ||
| 135 | * This method returns an image object that can be used to | ||
| 136 | * represent the bean in toolboxes, toolbars, etc. Icon images | ||
| 137 | * will typically be GIFs, but may in future include other formats. | ||
| 138 | * <p> | ||
| 139 | * Beans aren't required to provide icons and may return null from | ||
| 140 | * this method. | ||
| 141 | * <p> | ||
| 142 | * There are four possible flavors of icons (16x16 color, | ||
| 143 | * 32x32 color, 16x16 mono, 32x32 mono). If a bean choses to only | ||
| 144 | * support a single icon we recommend supporting 16x16 color. | ||
| 145 | * <p> | ||
| 146 | * We recommend that icons have a "transparent" background | ||
| 147 | * so they can be rendered onto an existing background. | ||
| 148 | * | ||
| 149 | * @param iconKind The kind of icon requested. This should be | ||
| 150 | * one of the constant values ICON_COLOR_16x16, ICON_COLOR_32x32, | ||
| 151 | * ICON_MONO_16x16, or ICON_MONO_32x32. | ||
| 152 | * @return An image object representing the requested icon. May | ||
| 153 | * return null if no suitable icon is available. | ||
| 154 | */ | ||
| 155 | java.awt.Image getIcon(int iconKind); | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Constant to indicate a 16 x 16 color icon. | ||
| 159 | */ | ||
| 160 | final static int ICON_COLOR_16x16 = 1; | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Constant to indicate a 32 x 32 color icon. | ||
| 164 | */ | ||
| 165 | final static int ICON_COLOR_32x32 = 2; | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Constant to indicate a 16 x 16 monochrome icon. | ||
| 169 | */ | ||
| 170 | final static int ICON_MONO_16x16 = 3; | ||
| 171 | |||
| 172 | /** | ||
| 173 | * Constant to indicate a 32 x 32 monochrome icon. | ||
| 174 | */ | ||
| 175 | final static int ICON_MONO_32x32 = 4; | ||
| 176 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,637 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import com.sun.beans.finder.ClassFinder; | ||
| 29 | |||
| 30 | import java.applet.Applet; | ||
| 31 | import java.applet.AppletContext; | ||
| 32 | import java.applet.AppletStub; | ||
| 33 | import java.applet.AudioClip; | ||
| 34 | |||
| 35 | import java.awt.GraphicsEnvironment; | ||
| 36 | import java.awt.Image; | ||
| 37 | |||
| 38 | import java.beans.beancontext.BeanContext; | ||
| 39 | |||
| 40 | import java.io.IOException; | ||
| 41 | import java.io.InputStream; | ||
| 42 | import java.io.ObjectInputStream; | ||
| 43 | import java.io.ObjectStreamClass; | ||
| 44 | import java.io.StreamCorruptedException; | ||
| 45 | |||
| 46 | import java.net.URL; | ||
| 47 | |||
| 48 | import java.security.AccessController; | ||
| 49 | import java.security.PrivilegedAction; | ||
| 50 | |||
| 51 | import java.util.Enumeration; | ||
| 52 | import java.util.Hashtable; | ||
| 53 | import java.util.Iterator; | ||
| 54 | import java.util.Vector; | ||
| 55 | |||
| 56 | import sun.awt.AppContext; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * This class provides some general purpose beans control methods. | ||
| 60 | */ | ||
| 61 | |||
| 62 | public class Beans { | ||
| 63 | private static final Object DESIGN_TIME = new Object(); | ||
| 64 | private static final Object GUI_AVAILABLE = new Object(); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * <p> | ||
| 68 | * Instantiate a JavaBean. | ||
| 69 | * </p> | ||
| 70 | * | ||
| 71 | * @param cls the class-loader from which we should create | ||
| 72 | * the bean. If this is null, then the system | ||
| 73 | * class-loader is used. | ||
| 74 | * @param beanName the name of the bean within the class-loader. | ||
| 75 | * For example "sun.beanbox.foobah" | ||
| 76 | * | ||
| 77 | * @exception ClassNotFoundException if the class of a serialized | ||
| 78 | * object could not be found. | ||
| 79 | * @exception IOException if an I/O error occurs. | ||
| 80 | */ | ||
| 81 | |||
| 82 | public static Object instantiate(ClassLoader cls, String beanName) throws IOException, ClassNotFoundException { | ||
| 83 | return Beans.instantiate(cls, beanName, null, null); | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * <p> | ||
| 88 | * Instantiate a JavaBean. | ||
| 89 | * </p> | ||
| 90 | * | ||
| 91 | * @param cls the class-loader from which we should create | ||
| 92 | * the bean. If this is null, then the system | ||
| 93 | * class-loader is used. | ||
| 94 | * @param beanName the name of the bean within the class-loader. | ||
| 95 | * For example "sun.beanbox.foobah" | ||
| 96 | * @param beanContext The BeanContext in which to nest the new bean | ||
| 97 | * | ||
| 98 | * @exception ClassNotFoundException if the class of a serialized | ||
| 99 | * object could not be found. | ||
| 100 | * @exception IOException if an I/O error occurs. | ||
| 101 | */ | ||
| 102 | |||
| 103 | public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext) throws IOException, ClassNotFoundException { | ||
| 104 | return Beans.instantiate(cls, beanName, beanContext, null); | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Instantiate a bean. | ||
| 109 | * <p> | ||
| 110 | * The bean is created based on a name relative to a class-loader. | ||
| 111 | * This name should be a dot-separated name such as "a.b.c". | ||
| 112 | * <p> | ||
| 113 | * In Beans 1.0 the given name can indicate either a serialized object | ||
| 114 | * or a class. Other mechanisms may be added in the future. In | ||
| 115 | * beans 1.0 we first try to treat the beanName as a serialized object | ||
| 116 | * name then as a class name. | ||
| 117 | * <p> | ||
| 118 | * When using the beanName as a serialized object name we convert the | ||
| 119 | * given beanName to a resource pathname and add a trailing ".ser" suffix. | ||
| 120 | * We then try to load a serialized object from that resource. | ||
| 121 | * <p> | ||
| 122 | * For example, given a beanName of "x.y", Beans.instantiate would first | ||
| 123 | * try to read a serialized object from the resource "x/y.ser" and if | ||
| 124 | * that failed it would try to load the class "x.y" and create an | ||
| 125 | * instance of that class. | ||
| 126 | * <p> | ||
| 127 | * If the bean is a subtype of java.applet.Applet, then it is given | ||
| 128 | * some special initialization. First, it is supplied with a default | ||
| 129 | * AppletStub and AppletContext. Second, if it was instantiated from | ||
| 130 | * a classname the applet's "init" method is called. (If the bean was | ||
| 131 | * deserialized this step is skipped.) | ||
| 132 | * <p> | ||
| 133 | * Note that for beans which are applets, it is the caller's responsiblity | ||
| 134 | * to call "start" on the applet. For correct behaviour, this should be done | ||
| 135 | * after the applet has been added into a visible AWT container. | ||
| 136 | * <p> | ||
| 137 | * Note that applets created via beans.instantiate run in a slightly | ||
| 138 | * different environment than applets running inside browsers. In | ||
| 139 | * particular, bean applets have no access to "parameters", so they may | ||
| 140 | * wish to provide property get/set methods to set parameter values. We | ||
| 141 | * advise bean-applet developers to test their bean-applets against both | ||
| 142 | * the JDK appletviewer (for a reference browser environment) and the | ||
| 143 | * BDK BeanBox (for a reference bean container). | ||
| 144 | * | ||
| 145 | * @param cls the class-loader from which we should create | ||
| 146 | * the bean. If this is null, then the system | ||
| 147 | * class-loader is used. | ||
| 148 | * @param beanName the name of the bean within the class-loader. | ||
| 149 | * For example "sun.beanbox.foobah" | ||
| 150 | * @param beanContext The BeanContext in which to nest the new bean | ||
| 151 | * @param initializer The AppletInitializer for the new bean | ||
| 152 | * | ||
| 153 | * @exception ClassNotFoundException if the class of a serialized | ||
| 154 | * object could not be found. | ||
| 155 | * @exception IOException if an I/O error occurs. | ||
| 156 | */ | ||
| 157 | |||
| 158 | public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer) | ||
| 159 | throws IOException, ClassNotFoundException { | ||
| 160 | |||
| 161 | InputStream ins; | ||
| 162 | ObjectInputStream oins = null; | ||
| 163 | Object result = null; | ||
| 164 | boolean serialized = false; | ||
| 165 | IOException serex = null; | ||
| 166 | |||
| 167 | // If the given classloader is null, we check if an | ||
| 168 | // system classloader is available and (if so) | ||
| 169 | // use that instead. | ||
| 170 | // Note that calls on the system class loader will | ||
| 171 | // look in the bootstrap class loader first. | ||
| 172 | if (cls == null) { | ||
| 173 | try { | ||
| 174 | cls = ClassLoader.getSystemClassLoader(); | ||
| 175 | } catch (SecurityException ex) { | ||
| 176 | // We're not allowed to access the system class loader. | ||
| 177 | // Drop through. | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | // Try to find a serialized object with this name | ||
| 182 | final String serName = beanName.replace('.','/').concat(".ser"); | ||
| 183 | final ClassLoader loader = cls; | ||
| 184 | ins = (InputStream)AccessController.doPrivileged | ||
| 185 | (new PrivilegedAction() { | ||
| 186 | public Object run() { | ||
| 187 | if (loader == null) | ||
| 188 | return ClassLoader.getSystemResourceAsStream(serName); | ||
| 189 | else | ||
| 190 | return loader.getResourceAsStream(serName); | ||
| 191 | } | ||
| 192 | }); | ||
| 193 | if (ins != null) { | ||
| 194 | try { | ||
| 195 | if (cls == null) { | ||
| 196 | oins = new ObjectInputStream(ins); | ||
| 197 | } else { | ||
| 198 | oins = new ObjectInputStreamWithLoader(ins, cls); | ||
| 199 | } | ||
| 200 | result = oins.readObject(); | ||
| 201 | serialized = true; | ||
| 202 | oins.close(); | ||
| 203 | } catch (IOException ex) { | ||
| 204 | ins.close(); | ||
| 205 | // Drop through and try opening the class. But remember | ||
| 206 | // the exception in case we can't find the class either. | ||
| 207 | serex = ex; | ||
| 208 | } catch (ClassNotFoundException ex) { | ||
| 209 | ins.close(); | ||
| 210 | throw ex; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | if (result == null) { | ||
| 215 | // No serialized object, try just instantiating the class | ||
| 216 | Class cl; | ||
| 217 | |||
| 218 | try { | ||
| 219 | cl = ClassFinder.findClass(beanName, cls); | ||
| 220 | } catch (ClassNotFoundException ex) { | ||
| 221 | // There is no appropriate class. If we earlier tried to | ||
| 222 | // deserialize an object and got an IO exception, throw that, | ||
| 223 | // otherwise rethrow the ClassNotFoundException. | ||
| 224 | if (serex != null) { | ||
| 225 | throw serex; | ||
| 226 | } | ||
| 227 | throw ex; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* | ||
| 231 | * Try to instantiate the class. | ||
| 232 | */ | ||
| 233 | |||
| 234 | try { | ||
| 235 | result = cl.newInstance(); | ||
| 236 | } catch (Exception ex) { | ||
| 237 | // We have to remap the exception to one in our signature. | ||
| 238 | // But we pass extra information in the detail message. | ||
| 239 | throw new ClassNotFoundException("" + cl + " : " + ex, ex); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | if (result != null) { | ||
| 244 | |||
| 245 | // Ok, if the result is an applet initialize it. | ||
| 246 | |||
| 247 | AppletStub stub = null; | ||
| 248 | |||
| 249 | if (result instanceof Applet) { | ||
| 250 | Applet applet = (Applet) result; | ||
| 251 | boolean needDummies = initializer == null; | ||
| 252 | |||
| 253 | if (needDummies) { | ||
| 254 | |||
| 255 | // Figure our the codebase and docbase URLs. We do this | ||
| 256 | // by locating the URL for a known resource, and then | ||
| 257 | // massaging the URL. | ||
| 258 | |||
| 259 | // First find the "resource name" corresponding to the bean | ||
| 260 | // itself. So a serialzied bean "a.b.c" would imply a | ||
| 261 | // resource name of "a/b/c.ser" and a classname of "x.y" | ||
| 262 | // would imply a resource name of "x/y.class". | ||
| 263 | |||
| 264 | final String resourceName; | ||
| 265 | |||
| 266 | if (serialized) { | ||
| 267 | // Serialized bean | ||
| 268 | resourceName = beanName.replace('.','/').concat(".ser"); | ||
| 269 | } else { | ||
| 270 | // Regular class | ||
| 271 | resourceName = beanName.replace('.','/').concat(".class"); | ||
| 272 | } | ||
| 273 | |||
| 274 | URL objectUrl = null; | ||
| 275 | URL codeBase = null; | ||
| 276 | URL docBase = null; | ||
| 277 | |||
| 278 | // Now get the URL correponding to the resource name. | ||
| 279 | |||
| 280 | final ClassLoader cloader = cls; | ||
| 281 | objectUrl = (URL) | ||
| 282 | AccessController.doPrivileged | ||
| 283 | (new PrivilegedAction() { | ||
| 284 | public Object run() { | ||
| 285 | if (cloader == null) | ||
| 286 | return ClassLoader.getSystemResource | ||
| 287 | (resourceName); | ||
| 288 | else | ||
| 289 | return cloader.getResource(resourceName); | ||
| 290 | } | ||
| 291 | }); | ||
| 292 | |||
| 293 | // If we found a URL, we try to locate the docbase by taking | ||
| 294 | // of the final path name component, and the code base by taking | ||
| 295 | // of the complete resourceName. | ||
| 296 | // So if we had a resourceName of "a/b/c.class" and we got an | ||
| 297 | // objectURL of "file://bert/classes/a/b/c.class" then we would | ||
| 298 | // want to set the codebase to "file://bert/classes/" and the | ||
| 299 | // docbase to "file://bert/classes/a/b/" | ||
| 300 | |||
| 301 | if (objectUrl != null) { | ||
| 302 | String s = objectUrl.toExternalForm(); | ||
| 303 | |||
| 304 | if (s.endsWith(resourceName)) { | ||
| 305 | int ix = s.length() - resourceName.length(); | ||
| 306 | codeBase = new URL(s.substring(0,ix)); | ||
| 307 | docBase = codeBase; | ||
| 308 | |||
| 309 | ix = s.lastIndexOf('/'); | ||
| 310 | |||
| 311 | if (ix >= 0) { | ||
| 312 | docBase = new URL(s.substring(0,ix+1)); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | // Setup a default context and stub. | ||
| 318 | BeansAppletContext context = new BeansAppletContext(applet); | ||
| 319 | |||
| 320 | stub = (AppletStub)new BeansAppletStub(applet, context, codeBase, docBase); | ||
| 321 | applet.setStub(stub); | ||
| 322 | } else { | ||
| 323 | initializer.initialize(applet, beanContext); | ||
| 324 | } | ||
| 325 | |||
| 326 | // now, if there is a BeanContext, add the bean, if applicable. | ||
| 327 | |||
| 328 | if (beanContext != null) { | ||
| 329 | beanContext.add(result); | ||
| 330 | } | ||
| 331 | |||
| 332 | // If it was deserialized then it was already init-ed. | ||
| 333 | // Otherwise we need to initialize it. | ||
| 334 | |||
| 335 | if (!serialized) { | ||
| 336 | // We need to set a reasonable initial size, as many | ||
| 337 | // applets are unhappy if they are started without | ||
| 338 | // having been explicitly sized. | ||
| 339 | applet.setSize(100,100); | ||
| 340 | applet.init(); | ||
| 341 | } | ||
| 342 | |||
| 343 | if (needDummies) { | ||
| 344 | ((BeansAppletStub)stub).active = true; | ||
| 345 | } else initializer.activate(applet); | ||
| 346 | |||
| 347 | } else if (beanContext != null) beanContext.add(result); | ||
| 348 | } | ||
| 349 | |||
| 350 | return result; | ||
| 351 | } | ||
| 352 | |||
| 353 | |||
| 354 | /** | ||
| 355 | * From a given bean, obtain an object representing a specified | ||
| 356 | * type view of that source object. | ||
| 357 | * <p> | ||
| 358 | * The result may be the same object or a different object. If | ||
| 359 | * the requested target view isn't available then the given | ||
| 360 | * bean is returned. | ||
| 361 | * <p> | ||
| 362 | * This method is provided in Beans 1.0 as a hook to allow the | ||
| 363 | * addition of more flexible bean behaviour in the future. | ||
| 364 | * | ||
| 365 | * @param bean Object from which we want to obtain a view. | ||
| 366 | * @param targetType The type of view we'd like to get. | ||
| 367 | * | ||
| 368 | */ | ||
| 369 | public static Object getInstanceOf(Object bean, Class<?> targetType) { | ||
| 370 | return bean; | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Check if a bean can be viewed as a given target type. | ||
| 375 | * The result will be true if the Beans.getInstanceof method | ||
| 376 | * can be used on the given bean to obtain an object that | ||
| 377 | * represents the specified targetType type view. | ||
| 378 | * | ||
| 379 | * @param bean Bean from which we want to obtain a view. | ||
| 380 | * @param targetType The type of view we'd like to get. | ||
| 381 | * @return "true" if the given bean supports the given targetType. | ||
| 382 | * | ||
| 383 | */ | ||
| 384 | public static boolean isInstanceOf(Object bean, Class<?> targetType) { | ||
| 385 | return Introspector.isSubclass(bean.getClass(), targetType); | ||
| 386 | } | ||
| 387 | |||
| 388 | |||
| 389 | /** | ||
| 390 | * Test if we are in design-mode. | ||
| 391 | * | ||
| 392 | * @return True if we are running in an application construction | ||
| 393 | * environment. | ||
| 394 | * | ||
| 395 | * @see DesignMode | ||
| 396 | */ | ||
| 397 | public static boolean isDesignTime() { | ||
| 398 | Object value = AppContext.getAppContext().get(DESIGN_TIME); | ||
| 399 | return (value instanceof Boolean) && (Boolean) value; | ||
| 400 | } | ||
| 401 | |||
| 402 | /** | ||
| 403 | * Determines whether beans can assume a GUI is available. | ||
| 404 | * | ||
| 405 | * @return True if we are running in an environment where beans | ||
| 406 | * can assume that an interactive GUI is available, so they | ||
| 407 | * can pop up dialog boxes, etc. This will normally return | ||
| 408 | * true in a windowing environment, and will normally return | ||
| 409 | * false in a server environment or if an application is | ||
| 410 | * running as part of a batch job. | ||
| 411 | * | ||
| 412 | * @see Visibility | ||
| 413 | * | ||
| 414 | */ | ||
| 415 | public static boolean isGuiAvailable() { | ||
| 416 | Object value = AppContext.getAppContext().get(GUI_AVAILABLE); | ||
| 417 | return (value instanceof Boolean) ? (Boolean) value : !GraphicsEnvironment.isHeadless(); | ||
| 418 | } | ||
| 419 | |||
| 420 | /** | ||
| 421 | * Used to indicate whether of not we are running in an application | ||
| 422 | * builder environment. | ||
| 423 | * | ||
| 424 | * <p>Note that this method is security checked | ||
| 425 | * and is not available to (for example) untrusted applets. | ||
| 426 | * More specifically, if there is a security manager, | ||
| 427 | * its <code>checkPropertiesAccess</code> | ||
| 428 | * method is called. This could result in a SecurityException. | ||
| 429 | * | ||
| 430 | * @param isDesignTime True if we're in an application builder tool. | ||
| 431 | * @exception SecurityException if a security manager exists and its | ||
| 432 | * <code>checkPropertiesAccess</code> method doesn't allow setting | ||
| 433 | * of system properties. | ||
| 434 | * @see SecurityManager#checkPropertiesAccess | ||
| 435 | */ | ||
| 436 | |||
| 437 | public static void setDesignTime(boolean isDesignTime) | ||
| 438 | throws SecurityException { | ||
| 439 | SecurityManager sm = System.getSecurityManager(); | ||
| 440 | if (sm != null) { | ||
| 441 | sm.checkPropertiesAccess(); | ||
| 442 | } | ||
| 443 | AppContext.getAppContext().put(DESIGN_TIME, Boolean.valueOf(isDesignTime)); | ||
| 444 | } | ||
| 445 | |||
| 446 | /** | ||
| 447 | * Used to indicate whether of not we are running in an environment | ||
| 448 | * where GUI interaction is available. | ||
| 449 | * | ||
| 450 | * <p>Note that this method is security checked | ||
| 451 | * and is not available to (for example) untrusted applets. | ||
| 452 | * More specifically, if there is a security manager, | ||
| 453 | * its <code>checkPropertiesAccess</code> | ||
| 454 | * method is called. This could result in a SecurityException. | ||
| 455 | * | ||
| 456 | * @param isGuiAvailable True if GUI interaction is available. | ||
| 457 | * @exception SecurityException if a security manager exists and its | ||
| 458 | * <code>checkPropertiesAccess</code> method doesn't allow setting | ||
| 459 | * of system properties. | ||
| 460 | * @see SecurityManager#checkPropertiesAccess | ||
| 461 | */ | ||
| 462 | |||
| 463 | public static void setGuiAvailable(boolean isGuiAvailable) | ||
| 464 | throws SecurityException { | ||
| 465 | SecurityManager sm = System.getSecurityManager(); | ||
| 466 | if (sm != null) { | ||
| 467 | sm.checkPropertiesAccess(); | ||
| 468 | } | ||
| 469 | AppContext.getAppContext().put(GUI_AVAILABLE, Boolean.valueOf(isGuiAvailable)); | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | /** | ||
| 474 | * This subclass of ObjectInputStream delegates loading of classes to | ||
| 475 | * an existing ClassLoader. | ||
| 476 | */ | ||
| 477 | |||
| 478 | class ObjectInputStreamWithLoader extends ObjectInputStream | ||
| 479 | { | ||
| 480 | private ClassLoader loader; | ||
| 481 | |||
| 482 | /** | ||
| 483 | * Loader must be non-null; | ||
| 484 | */ | ||
| 485 | |||
| 486 | public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader) | ||
| 487 | throws IOException, StreamCorruptedException { | ||
| 488 | |||
| 489 | super(in); | ||
| 490 | if (loader == null) { | ||
| 491 | throw new IllegalArgumentException("Illegal null argument to ObjectInputStreamWithLoader"); | ||
| 492 | } | ||
| 493 | this.loader = loader; | ||
| 494 | } | ||
| 495 | |||
| 496 | /** | ||
| 497 | * Use the given ClassLoader rather than using the system class | ||
| 498 | */ | ||
| 499 | protected Class resolveClass(ObjectStreamClass classDesc) | ||
| 500 | throws IOException, ClassNotFoundException { | ||
| 501 | |||
| 502 | String cname = classDesc.getName(); | ||
| 503 | return ClassFinder.resolveClass(cname, this.loader); | ||
| 504 | } | ||
| 505 | } | ||
| 506 | |||
| 507 | /** | ||
| 508 | * Package private support class. This provides a default AppletContext | ||
| 509 | * for beans which are applets. | ||
| 510 | */ | ||
| 511 | |||
| 512 | class BeansAppletContext implements AppletContext { | ||
| 513 | Applet target; | ||
| 514 | Hashtable imageCache = new Hashtable(); | ||
| 515 | |||
| 516 | BeansAppletContext(Applet target) { | ||
| 517 | this.target = target; | ||
| 518 | } | ||
| 519 | |||
| 520 | public AudioClip getAudioClip(URL url) { | ||
| 521 | // We don't currently support audio clips in the Beans.instantiate | ||
| 522 | // applet context, unless by some luck there exists a URL content | ||
| 523 | // class that can generate an AudioClip from the audio URL. | ||
| 524 | try { | ||
| 525 | return (AudioClip) url.getContent(); | ||
| 526 | } catch (Exception ex) { | ||
| 527 | return null; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | public synchronized Image getImage(URL url) { | ||
| 532 | Object o = imageCache.get(url); | ||
| 533 | if (o != null) { | ||
| 534 | return (Image)o; | ||
| 535 | } | ||
| 536 | try { | ||
| 537 | o = url.getContent(); | ||
| 538 | if (o == null) { | ||
| 539 | return null; | ||
| 540 | } | ||
| 541 | if (o instanceof Image) { | ||
| 542 | imageCache.put(url, o); | ||
| 543 | return (Image) o; | ||
| 544 | } | ||
| 545 | // Otherwise it must be an ImageProducer. | ||
| 546 | Image img = target.createImage((java.awt.image.ImageProducer)o); | ||
| 547 | imageCache.put(url, img); | ||
| 548 | return img; | ||
| 549 | |||
| 550 | } catch (Exception ex) { | ||
| 551 | return null; | ||
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 | public Applet getApplet(String name) { | ||
| 556 | return null; | ||
| 557 | } | ||
| 558 | |||
| 559 | public Enumeration getApplets() { | ||
| 560 | Vector applets = new Vector(); | ||
| 561 | applets.addElement(target); | ||
| 562 | return applets.elements(); | ||
| 563 | } | ||
| 564 | |||
| 565 | public void showDocument(URL url) { | ||
| 566 | // We do nothing. | ||
| 567 | } | ||
| 568 | |||
| 569 | public void showDocument(URL url, String target) { | ||
| 570 | // We do nothing. | ||
| 571 | } | ||
| 572 | |||
| 573 | public void showStatus(String status) { | ||
| 574 | // We do nothing. | ||
| 575 | } | ||
| 576 | |||
| 577 | public void setStream(String key, InputStream stream)throws IOException{ | ||
| 578 | // We do nothing. | ||
| 579 | } | ||
| 580 | |||
| 581 | public InputStream getStream(String key){ | ||
| 582 | // We do nothing. | ||
| 583 | return null; | ||
| 584 | } | ||
| 585 | |||
| 586 | public Iterator getStreamKeys(){ | ||
| 587 | // We do nothing. | ||
| 588 | return null; | ||
| 589 | } | ||
| 590 | } | ||
| 591 | |||
| 592 | /** | ||
| 593 | * Package private support class. This provides an AppletStub | ||
| 594 | * for beans which are applets. | ||
| 595 | */ | ||
| 596 | class BeansAppletStub implements AppletStub { | ||
| 597 | transient boolean active; | ||
| 598 | transient Applet target; | ||
| 599 | transient AppletContext context; | ||
| 600 | transient URL codeBase; | ||
| 601 | transient URL docBase; | ||
| 602 | |||
| 603 | BeansAppletStub(Applet target, | ||
| 604 | AppletContext context, URL codeBase, | ||
| 605 | URL docBase) { | ||
| 606 | this.target = target; | ||
| 607 | this.context = context; | ||
| 608 | this.codeBase = codeBase; | ||
| 609 | this.docBase = docBase; | ||
| 610 | } | ||
| 611 | |||
| 612 | public boolean isActive() { | ||
| 613 | return active; | ||
| 614 | } | ||
| 615 | |||
| 616 | public URL getDocumentBase() { | ||
| 617 | // use the root directory of the applet's class-loader | ||
| 618 | return docBase; | ||
| 619 | } | ||
| 620 | |||
| 621 | public URL getCodeBase() { | ||
| 622 | // use the directory where we found the class or serialized object. | ||
| 623 | return codeBase; | ||
| 624 | } | ||
| 625 | |||
| 626 | public String getParameter(String name) { | ||
| 627 | return null; | ||
| 628 | } | ||
| 629 | |||
| 630 | public AppletContext getAppletContext() { | ||
| 631 | return context; | ||
| 632 | } | ||
| 633 | |||
| 634 | public void appletResize(int width, int height) { | ||
| 635 | // we do nothing. | ||
| 636 | } | ||
| 637 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,247 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.util.ArrayList; | ||
| 28 | import java.util.Collections; | ||
| 29 | import java.util.EventListener; | ||
| 30 | import java.util.EventListenerProxy; | ||
| 31 | import java.util.HashMap; | ||
| 32 | import java.util.List; | ||
| 33 | import java.util.Map; | ||
| 34 | import java.util.Map.Entry; | ||
| 35 | import java.util.Set; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * This is an abstract class that provides base functionality | ||
| 39 | * for the {@link PropertyChangeSupport PropertyChangeSupport} class | ||
| 40 | * and the {@link VetoableChangeSupport VetoableChangeSupport} class. | ||
| 41 | * | ||
| 42 | * @see PropertyChangeListenerMap | ||
| 43 | * @see VetoableChangeListenerMap | ||
| 44 | * | ||
| 45 | * @author Sergey A. Malenkov | ||
| 46 | */ | ||
| 47 | abstract class ChangeListenerMap<L extends EventListener> { | ||
| 48 | private Map<String, L[]> map; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Creates an array of listeners. | ||
| 52 | * This method can be optimized by using | ||
| 53 | * the same instance of the empty array | ||
| 54 | * when {@code length} is equal to {@code 0}. | ||
| 55 | * | ||
| 56 | * @param length the array length | ||
| 57 | * @return an array with specified length | ||
| 58 | */ | ||
| 59 | protected abstract L[] newArray(int length); | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Creates a proxy listener for the specified property. | ||
| 63 | * | ||
| 64 | * @param name the name of the property to listen on | ||
| 65 | * @param listener the listener to process events | ||
| 66 | * @return a proxy listener | ||
| 67 | */ | ||
| 68 | protected abstract L newProxy(String name, L listener); | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Adds a listener to the list of listeners for the specified property. | ||
| 72 | * This listener is called as many times as it was added. | ||
| 73 | * | ||
| 74 | * @param name the name of the property to listen on | ||
| 75 | * @param listener the listener to process events | ||
| 76 | */ | ||
| 77 | public final synchronized void add(String name, L listener) { | ||
| 78 | if (this.map == null) { | ||
| 79 | this.map = new HashMap<String, L[]>(); | ||
| 80 | } | ||
| 81 | L[] array = this.map.get(name); | ||
| 82 | int size = (array != null) | ||
| 83 | ? array.length | ||
| 84 | : 0; | ||
| 85 | |||
| 86 | L[] clone = newArray(size + 1); | ||
| 87 | clone[size] = listener; | ||
| 88 | if (array != null) { | ||
| 89 | System.arraycopy(array, 0, clone, 0, size); | ||
| 90 | } | ||
| 91 | this.map.put(name, clone); | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Removes a listener from the list of listeners for the specified property. | ||
| 96 | * If the listener was added more than once to the same event source, | ||
| 97 | * this listener will be notified one less time after being removed. | ||
| 98 | * | ||
| 99 | * @param name the name of the property to listen on | ||
| 100 | * @param listener the listener to process events | ||
| 101 | */ | ||
| 102 | public final synchronized void remove(String name, L listener) { | ||
| 103 | if (this.map != null) { | ||
| 104 | L[] array = this.map.get(name); | ||
| 105 | if (array != null) { | ||
| 106 | for (int i = 0; i < array.length; i++) { | ||
| 107 | if (listener.equals(array[i])) { | ||
| 108 | int size = array.length - 1; | ||
| 109 | if (size > 0) { | ||
| 110 | L[] clone = newArray(size); | ||
| 111 | System.arraycopy(array, 0, clone, 0, i); | ||
| 112 | System.arraycopy(array, i + 1, clone, i, size - i); | ||
| 113 | this.map.put(name, clone); | ||
| 114 | } | ||
| 115 | else { | ||
| 116 | this.map.remove(name); | ||
| 117 | if (this.map.isEmpty()) { | ||
| 118 | this.map = null; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * Returns the list of listeners for the specified property. | ||
| 130 | * | ||
| 131 | * @param name the name of the property | ||
| 132 | * @return the corresponding list of listeners | ||
| 133 | */ | ||
| 134 | public final synchronized L[] get(String name) { | ||
| 135 | return (this.map != null) | ||
| 136 | ? this.map.get(name) | ||
| 137 | : null; | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Sets new list of listeners for the specified property. | ||
| 142 | * | ||
| 143 | * @param name the name of the property | ||
| 144 | * @param listeners new list of listeners | ||
| 145 | */ | ||
| 146 | public final void set(String name, L[] listeners) { | ||
| 147 | if (listeners != null) { | ||
| 148 | if (this.map == null) { | ||
| 149 | this.map = new HashMap<String, L[]>(); | ||
| 150 | } | ||
| 151 | this.map.put(name, listeners); | ||
| 152 | } | ||
| 153 | else if (this.map != null) { | ||
| 154 | this.map.remove(name); | ||
| 155 | if (this.map.isEmpty()) { | ||
| 156 | this.map = null; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Returns all listeners in the map. | ||
| 163 | * | ||
| 164 | * @return an array of all listeners | ||
| 165 | */ | ||
| 166 | public final synchronized L[] getListeners() { | ||
| 167 | if (this.map == null) { | ||
| 168 | return newArray(0); | ||
| 169 | } | ||
| 170 | List<L> list = new ArrayList<L>(); | ||
| 171 | |||
| 172 | L[] listeners = this.map.get(null); | ||
| 173 | if (listeners != null) { | ||
| 174 | for (L listener : listeners) { | ||
| 175 | list.add(listener); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | for (Entry<String, L[]> entry : this.map.entrySet()) { | ||
| 179 | String name = entry.getKey(); | ||
| 180 | if (name != null) { | ||
| 181 | for (L listener : entry.getValue()) { | ||
| 182 | list.add(newProxy(name, listener)); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | return list.toArray(newArray(list.size())); | ||
| 187 | } | ||
| 188 | |||
| 189 | /** | ||
| 190 | * Returns listeners that have been associated with the named property. | ||
| 191 | * | ||
| 192 | * @param name the name of the property | ||
| 193 | * @return an array of listeners for the named property | ||
| 194 | */ | ||
| 195 | public final L[] getListeners(String name) { | ||
| 196 | if (name != null) { | ||
| 197 | L[] listeners = get(name); | ||
| 198 | if (listeners != null) { | ||
| 199 | return listeners.clone(); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | return newArray(0); | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 206 | * Indicates whether the map contains | ||
| 207 | * at least one listener to be notified. | ||
| 208 | * | ||
| 209 | * @param name the name of the property | ||
| 210 | * @return {@code true} if at least one listener exists or | ||
| 211 | * {@code false} otherwise | ||
| 212 | */ | ||
| 213 | public final synchronized boolean hasListeners(String name) { | ||
| 214 | if (this.map == null) { | ||
| 215 | return false; | ||
| 216 | } | ||
| 217 | L[] array = this.map.get(null); | ||
| 218 | return (array != null) || ((name != null) && (null != this.map.get(name))); | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * Returns a set of entries from the map. | ||
| 223 | * Each entry is a pair consisted of the property name | ||
| 224 | * and the corresponding list of listeners. | ||
| 225 | * | ||
| 226 | * @return a set of entries from the map | ||
| 227 | */ | ||
| 228 | public final Set<Entry<String, L[]>> getEntries() { | ||
| 229 | return (this.map != null) | ||
| 230 | ? this.map.entrySet() | ||
| 231 | : Collections.<Entry<String, L[]>>emptySet(); | ||
| 232 | } | ||
| 233 | |||
| 234 | /** | ||
| 235 | * Extracts a real listener from the proxy listener. | ||
| 236 | * It is necessary because default proxy class is not serializable. | ||
| 237 | * | ||
| 238 | * @return a real listener | ||
| 239 | */ | ||
| 240 | public final L extract(L listener) { | ||
| 241 | while (listener instanceof EventListenerProxy) { | ||
| 242 | EventListenerProxy<L> proxy = (EventListenerProxy<L>) listener; | ||
| 243 | listener = proxy.getListener(); | ||
| 244 | } | ||
| 245 | return listener; | ||
| 246 | } | ||
| 247 | } | ||
Binary file not shown.
| @@ -0,0 +1,76 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.lang.annotation.*; | ||
| 29 | import static java.lang.annotation.ElementType.*; | ||
| 30 | import static java.lang.annotation.RetentionPolicy.*; | ||
| 31 | |||
| 32 | /** | ||
| 33 | <p>An annotation on a constructor that shows how the parameters of | ||
| 34 | that constructor correspond to the constructed object's getter | ||
| 35 | methods. For example: | ||
| 36 | |||
| 37 | <blockquote> | ||
| 38 | <pre> | ||
| 39 | public class Point { | ||
| 40 | @ConstructorProperties({"x", "y"}) | ||
| 41 | public Point(int x, int y) { | ||
| 42 | this.x = x; | ||
| 43 | this.y = y; | ||
| 44 | } | ||
| 45 | |||
| 46 | public int getX() { | ||
| 47 | return x; | ||
| 48 | } | ||
| 49 | |||
| 50 | public int getY() { | ||
| 51 | return y; | ||
| 52 | } | ||
| 53 | |||
| 54 | private final int x, y; | ||
| 55 | } | ||
| 56 | </pre> | ||
| 57 | </blockquote> | ||
| 58 | |||
| 59 | The annotation shows that the first parameter of the constructor | ||
| 60 | can be retrieved with the {@code getX()} method and the second with | ||
| 61 | the {@code getY()} method. Since parameter names are not in | ||
| 62 | general available at runtime, without the annotation there would be | ||
| 63 | no way to know whether the parameters correspond to {@code getX()} | ||
| 64 | and {@code getY()} or the other way around.</p> | ||
| 65 | |||
| 66 | @since 1.6 | ||
| 67 | */ | ||
| 68 | @Documented @Target(CONSTRUCTOR) @Retention(RUNTIME) | ||
| 69 | public @interface ConstructorProperties { | ||
| 70 | /** | ||
| 71 | <p>The getter names.</p> | ||
| 72 | @return the getter names corresponding to the parameters in the | ||
| 73 | annotated constructor. | ||
| 74 | */ | ||
| 75 | String[] value(); | ||
| 76 | } | ||
Binary file not shown.
| @@ -0,0 +1,65 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A customizer class provides a complete custom GUI for customizing | ||
| 30 | * a target Java Bean. | ||
| 31 | * <P> | ||
| 32 | * Each customizer should inherit from the java.awt.Component class so | ||
| 33 | * it can be instantiated inside an AWT dialog or panel. | ||
| 34 | * <P> | ||
| 35 | * Each customizer should have a null constructor. | ||
| 36 | */ | ||
| 37 | |||
| 38 | public interface Customizer { | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Set the object to be customized. This method should be called only | ||
| 42 | * once, before the Customizer has been added to any parent AWT container. | ||
| 43 | * @param bean The object to be customized. | ||
| 44 | */ | ||
| 45 | void setObject(Object bean); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Register a listener for the PropertyChange event. The customizer | ||
| 49 | * should fire a PropertyChange event whenever it changes the target | ||
| 50 | * bean in a way that might require the displayed properties to be | ||
| 51 | * refreshed. | ||
| 52 | * | ||
| 53 | * @param listener An object to be invoked when a PropertyChange | ||
| 54 | * event is fired. | ||
| 55 | */ | ||
| 56 | void addPropertyChangeListener(PropertyChangeListener listener); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Remove a listener for the PropertyChange event. | ||
| 60 | * | ||
| 61 | * @param listener The PropertyChange listener to be removed. | ||
| 62 | */ | ||
| 63 | void removePropertyChangeListener(PropertyChangeListener listener); | ||
| 64 | |||
| 65 | } | ||
Binary file not shown.
| @@ -0,0 +1,414 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.util.*; | ||
| 28 | import java.lang.reflect.*; | ||
| 29 | import java.util.Objects; | ||
| 30 | import sun.reflect.misc.*; | ||
| 31 | |||
| 32 | |||
| 33 | /** | ||
| 34 | * The <code>DefaultPersistenceDelegate</code> is a concrete implementation of | ||
| 35 | * the abstract <code>PersistenceDelegate</code> class and | ||
| 36 | * is the delegate used by default for classes about | ||
| 37 | * which no information is available. The <code>DefaultPersistenceDelegate</code> | ||
| 38 | * provides, version resilient, public API-based persistence for | ||
| 39 | * classes that follow the JavaBeans™ conventions without any class specific | ||
| 40 | * configuration. | ||
| 41 | * <p> | ||
| 42 | * The key assumptions are that the class has a nullary constructor | ||
| 43 | * and that its state is accurately represented by matching pairs | ||
| 44 | * of "setter" and "getter" methods in the order they are returned | ||
| 45 | * by the Introspector. | ||
| 46 | * In addition to providing code-free persistence for JavaBeans, | ||
| 47 | * the <code>DefaultPersistenceDelegate</code> provides a convenient means | ||
| 48 | * to effect persistent storage for classes that have a constructor | ||
| 49 | * that, while not nullary, simply requires some property values | ||
| 50 | * as arguments. | ||
| 51 | * | ||
| 52 | * @see #DefaultPersistenceDelegate(String[]) | ||
| 53 | * @see java.beans.Introspector | ||
| 54 | * | ||
| 55 | * @since 1.4 | ||
| 56 | * | ||
| 57 | * @author Philip Milne | ||
| 58 | */ | ||
| 59 | |||
| 60 | public class DefaultPersistenceDelegate extends PersistenceDelegate { | ||
| 61 | private String[] constructor; | ||
| 62 | private Boolean definesEquals; | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Creates a persistence delegate for a class with a nullary constructor. | ||
| 66 | * | ||
| 67 | * @see #DefaultPersistenceDelegate(java.lang.String[]) | ||
| 68 | */ | ||
| 69 | public DefaultPersistenceDelegate() { | ||
| 70 | this(new String[0]); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Creates a default persistence delegate for a class with a | ||
| 75 | * constructor whose arguments are the values of the property | ||
| 76 | * names as specified by <code>constructorPropertyNames</code>. | ||
| 77 | * The constructor arguments are created by | ||
| 78 | * evaluating the property names in the order they are supplied. | ||
| 79 | * To use this class to specify a single preferred constructor for use | ||
| 80 | * in the serialization of a particular type, we state the | ||
| 81 | * names of the properties that make up the constructor's | ||
| 82 | * arguments. For example, the <code>Font</code> class which | ||
| 83 | * does not define a nullary constructor can be handled | ||
| 84 | * with the following persistence delegate: | ||
| 85 | * | ||
| 86 | * <pre> | ||
| 87 | * new DefaultPersistenceDelegate(new String[]{"name", "style", "size"}); | ||
| 88 | * </pre> | ||
| 89 | * | ||
| 90 | * @param constructorPropertyNames The property names for the arguments of this constructor. | ||
| 91 | * | ||
| 92 | * @see #instantiate | ||
| 93 | */ | ||
| 94 | public DefaultPersistenceDelegate(String[] constructorPropertyNames) { | ||
| 95 | this.constructor = constructorPropertyNames; | ||
| 96 | } | ||
| 97 | |||
| 98 | private static boolean definesEquals(Class type) { | ||
| 99 | try { | ||
| 100 | return type == type.getMethod("equals", Object.class).getDeclaringClass(); | ||
| 101 | } | ||
| 102 | catch(NoSuchMethodException e) { | ||
| 103 | return false; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | private boolean definesEquals(Object instance) { | ||
| 108 | if (definesEquals != null) { | ||
| 109 | return (definesEquals == Boolean.TRUE); | ||
| 110 | } | ||
| 111 | else { | ||
| 112 | boolean result = definesEquals(instance.getClass()); | ||
| 113 | definesEquals = result ? Boolean.TRUE : Boolean.FALSE; | ||
| 114 | return result; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | /** | ||
| 119 | * If the number of arguments in the specified constructor is non-zero and | ||
| 120 | * the class of <code>oldInstance</code> explicitly declares an "equals" method | ||
| 121 | * this method returns the value of <code>oldInstance.equals(newInstance)</code>. | ||
| 122 | * Otherwise, this method uses the superclass's definition which returns true if the | ||
| 123 | * classes of the two instances are equal. | ||
| 124 | * | ||
| 125 | * @param oldInstance The instance to be copied. | ||
| 126 | * @param newInstance The instance that is to be modified. | ||
| 127 | * @return True if an equivalent copy of <code>newInstance</code> may be | ||
| 128 | * created by applying a series of mutations to <code>oldInstance</code>. | ||
| 129 | * | ||
| 130 | * @see #DefaultPersistenceDelegate(String[]) | ||
| 131 | */ | ||
| 132 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 133 | // Assume the instance is either mutable or a singleton | ||
| 134 | // if it has a nullary constructor. | ||
| 135 | return (constructor.length == 0) || !definesEquals(oldInstance) ? | ||
| 136 | super.mutatesTo(oldInstance, newInstance) : | ||
| 137 | oldInstance.equals(newInstance); | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * This default implementation of the <code>instantiate</code> method returns | ||
| 142 | * an expression containing the predefined method name "new" which denotes a | ||
| 143 | * call to a constructor with the arguments as specified in | ||
| 144 | * the <code>DefaultPersistenceDelegate</code>'s constructor. | ||
| 145 | * | ||
| 146 | * @param oldInstance The instance to be instantiated. | ||
| 147 | * @param out The code output stream. | ||
| 148 | * @return An expression whose value is <code>oldInstance</code>. | ||
| 149 | * | ||
| 150 | * @throws NullPointerException if {@code out} is {@code null} | ||
| 151 | * | ||
| 152 | * @see #DefaultPersistenceDelegate(String[]) | ||
| 153 | */ | ||
| 154 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 155 | int nArgs = constructor.length; | ||
| 156 | Class type = oldInstance.getClass(); | ||
| 157 | Object[] constructorArgs = new Object[nArgs]; | ||
| 158 | for(int i = 0; i < nArgs; i++) { | ||
| 159 | try { | ||
| 160 | Method method = findMethod(type, this.constructor[i]); | ||
| 161 | constructorArgs[i] = MethodUtil.invoke(method, oldInstance, new Object[0]); | ||
| 162 | } | ||
| 163 | catch (Exception e) { | ||
| 164 | out.getExceptionListener().exceptionThrown(e); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | return new Expression(oldInstance, oldInstance.getClass(), "new", constructorArgs); | ||
| 168 | } | ||
| 169 | |||
| 170 | private Method findMethod(Class type, String property) { | ||
| 171 | if (property == null) { | ||
| 172 | throw new IllegalArgumentException("Property name is null"); | ||
| 173 | } | ||
| 174 | PropertyDescriptor pd = getPropertyDescriptor(type, property); | ||
| 175 | if (pd == null) { | ||
| 176 | throw new IllegalStateException("Could not find property by the name " + property); | ||
| 177 | } | ||
| 178 | Method method = pd.getReadMethod(); | ||
| 179 | if (method == null) { | ||
| 180 | throw new IllegalStateException("Could not find getter for the property " + property); | ||
| 181 | } | ||
| 182 | return method; | ||
| 183 | } | ||
| 184 | |||
| 185 | private void doProperty(Class type, PropertyDescriptor pd, Object oldInstance, Object newInstance, Encoder out) throws Exception { | ||
| 186 | Method getter = pd.getReadMethod(); | ||
| 187 | Method setter = pd.getWriteMethod(); | ||
| 188 | |||
| 189 | if (getter != null && setter != null) { | ||
| 190 | Expression oldGetExp = new Expression(oldInstance, getter.getName(), new Object[]{}); | ||
| 191 | Expression newGetExp = new Expression(newInstance, getter.getName(), new Object[]{}); | ||
| 192 | Object oldValue = oldGetExp.getValue(); | ||
| 193 | Object newValue = newGetExp.getValue(); | ||
| 194 | out.writeExpression(oldGetExp); | ||
| 195 | if (!Objects.equals(newValue, out.get(oldValue))) { | ||
| 196 | // Search for a static constant with this value; | ||
| 197 | Object e = (Object[])pd.getValue("enumerationValues"); | ||
| 198 | if (e instanceof Object[] && Array.getLength(e) % 3 == 0) { | ||
| 199 | Object[] a = (Object[])e; | ||
| 200 | for(int i = 0; i < a.length; i = i + 3) { | ||
| 201 | try { | ||
| 202 | Field f = type.getField((String)a[i]); | ||
| 203 | if (f.get(null).equals(oldValue)) { | ||
| 204 | out.remove(oldValue); | ||
| 205 | out.writeExpression(new Expression(oldValue, f, "get", new Object[]{null})); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | catch (Exception ex) {} | ||
| 209 | } | ||
| 210 | } | ||
| 211 | invokeStatement(oldInstance, setter.getName(), new Object[]{oldValue}, out); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | static void invokeStatement(Object instance, String methodName, Object[] args, Encoder out) { | ||
| 217 | out.writeStatement(new Statement(instance, methodName, args)); | ||
| 218 | } | ||
| 219 | |||
| 220 | // Write out the properties of this instance. | ||
| 221 | private void initBean(Class type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 222 | for (Field field : type.getFields()) { | ||
| 223 | int mod = field.getModifiers(); | ||
| 224 | if (Modifier.isFinal(mod) || Modifier.isStatic(mod) || Modifier.isTransient(mod)) { | ||
| 225 | continue; | ||
| 226 | } | ||
| 227 | try { | ||
| 228 | Expression oldGetExp = new Expression(field, "get", new Object[] { oldInstance }); | ||
| 229 | Expression newGetExp = new Expression(field, "get", new Object[] { newInstance }); | ||
| 230 | Object oldValue = oldGetExp.getValue(); | ||
| 231 | Object newValue = newGetExp.getValue(); | ||
| 232 | out.writeExpression(oldGetExp); | ||
| 233 | if (!Objects.equals(newValue, out.get(oldValue))) { | ||
| 234 | out.writeStatement(new Statement(field, "set", new Object[] { oldInstance, oldValue })); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | catch (Exception exception) { | ||
| 238 | out.getExceptionListener().exceptionThrown(exception); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | BeanInfo info; | ||
| 242 | try { | ||
| 243 | info = Introspector.getBeanInfo(type); | ||
| 244 | } catch (IntrospectionException exception) { | ||
| 245 | return; | ||
| 246 | } | ||
| 247 | // Properties | ||
| 248 | for (PropertyDescriptor d : info.getPropertyDescriptors()) { | ||
| 249 | if (d.isTransient()) { | ||
| 250 | continue; | ||
| 251 | } | ||
| 252 | try { | ||
| 253 | doProperty(type, d, oldInstance, newInstance, out); | ||
| 254 | } | ||
| 255 | catch (Exception e) { | ||
| 256 | out.getExceptionListener().exceptionThrown(e); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | // Listeners | ||
| 261 | /* | ||
| 262 | Pending(milne). There is a general problem with the archival of | ||
| 263 | listeners which is unresolved as of 1.4. Many of the methods | ||
| 264 | which install one object inside another (typically "add" methods | ||
| 265 | or setters) automatically install a listener on the "child" object | ||
| 266 | so that its "parent" may respond to changes that are made to it. | ||
| 267 | For example the JTable:setModel() method automatically adds a | ||
| 268 | TableModelListener (the JTable itself in this case) to the supplied | ||
| 269 | table model. | ||
| 270 | |||
| 271 | We do not need to explictly add these listeners to the model in an | ||
| 272 | archive as they will be added automatically by, in the above case, | ||
| 273 | the JTable's "setModel" method. In some cases, we must specifically | ||
| 274 | avoid trying to do this since the listener may be an inner class | ||
| 275 | that cannot be instantiated using public API. | ||
| 276 | |||
| 277 | No general mechanism currently | ||
| 278 | exists for differentiating between these kind of listeners and | ||
| 279 | those which were added explicitly by the user. A mechanism must | ||
| 280 | be created to provide a general means to differentiate these | ||
| 281 | special cases so as to provide reliable persistence of listeners | ||
| 282 | for the general case. | ||
| 283 | */ | ||
| 284 | if (!java.awt.Component.class.isAssignableFrom(type)) { | ||
| 285 | return; // Just handle the listeners of Components for now. | ||
| 286 | } | ||
| 287 | for (EventSetDescriptor d : info.getEventSetDescriptors()) { | ||
| 288 | if (d.isTransient()) { | ||
| 289 | continue; | ||
| 290 | } | ||
| 291 | Class listenerType = d.getListenerType(); | ||
| 292 | |||
| 293 | |||
| 294 | // The ComponentListener is added automatically, when | ||
| 295 | // Contatiner:add is called on the parent. | ||
| 296 | if (listenerType == java.awt.event.ComponentListener.class) { | ||
| 297 | continue; | ||
| 298 | } | ||
| 299 | |||
| 300 | // JMenuItems have a change listener added to them in | ||
| 301 | // their "add" methods to enable accessibility support - | ||
| 302 | // see the add method in JMenuItem for details. We cannot | ||
| 303 | // instantiate this instance as it is a private inner class | ||
| 304 | // and do not need to do this anyway since it will be created | ||
| 305 | // and installed by the "add" method. Special case this for now, | ||
| 306 | // ignoring all change listeners on JMenuItems. | ||
| 307 | if (listenerType == javax.swing.event.ChangeListener.class && | ||
| 308 | type == javax.swing.JMenuItem.class) { | ||
| 309 | continue; | ||
| 310 | } | ||
| 311 | |||
| 312 | EventListener[] oldL = new EventListener[0]; | ||
| 313 | EventListener[] newL = new EventListener[0]; | ||
| 314 | try { | ||
| 315 | Method m = d.getGetListenerMethod(); | ||
| 316 | oldL = (EventListener[])MethodUtil.invoke(m, oldInstance, new Object[]{}); | ||
| 317 | newL = (EventListener[])MethodUtil.invoke(m, newInstance, new Object[]{}); | ||
| 318 | } | ||
| 319 | catch (Exception e2) { | ||
| 320 | try { | ||
| 321 | Method m = type.getMethod("getListeners", new Class[]{Class.class}); | ||
| 322 | oldL = (EventListener[])MethodUtil.invoke(m, oldInstance, new Object[]{listenerType}); | ||
| 323 | newL = (EventListener[])MethodUtil.invoke(m, newInstance, new Object[]{listenerType}); | ||
| 324 | } | ||
| 325 | catch (Exception e3) { | ||
| 326 | return; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | // Asssume the listeners are in the same order and that there are no gaps. | ||
| 331 | // Eventually, this may need to do true differencing. | ||
| 332 | String addListenerMethodName = d.getAddListenerMethod().getName(); | ||
| 333 | for (int i = newL.length; i < oldL.length; i++) { | ||
| 334 | // System.out.println("Adding listener: " + addListenerMethodName + oldL[i]); | ||
| 335 | invokeStatement(oldInstance, addListenerMethodName, new Object[]{oldL[i]}, out); | ||
| 336 | } | ||
| 337 | |||
| 338 | String removeListenerMethodName = d.getRemoveListenerMethod().getName(); | ||
| 339 | for (int i = oldL.length; i < newL.length; i++) { | ||
| 340 | invokeStatement(oldInstance, removeListenerMethodName, new Object[]{newL[i]}, out); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | /** | ||
| 346 | * This default implementation of the <code>initialize</code> method assumes | ||
| 347 | * all state held in objects of this type is exposed via the | ||
| 348 | * matching pairs of "setter" and "getter" methods in the order | ||
| 349 | * they are returned by the Introspector. If a property descriptor | ||
| 350 | * defines a "transient" attribute with a value equal to | ||
| 351 | * <code>Boolean.TRUE</code> the property is ignored by this | ||
| 352 | * default implementation. Note that this use of the word | ||
| 353 | * "transient" is quite independent of the field modifier | ||
| 354 | * that is used by the <code>ObjectOutputStream</code>. | ||
| 355 | * <p> | ||
| 356 | * For each non-transient property, an expression is created | ||
| 357 | * in which the nullary "getter" method is applied | ||
| 358 | * to the <code>oldInstance</code>. The value of this | ||
| 359 | * expression is the value of the property in the instance that is | ||
| 360 | * being serialized. If the value of this expression | ||
| 361 | * in the cloned environment <code>mutatesTo</code> the | ||
| 362 | * target value, the new value is initialized to make it | ||
| 363 | * equivalent to the old value. In this case, because | ||
| 364 | * the property value has not changed there is no need to | ||
| 365 | * call the corresponding "setter" method and no statement | ||
| 366 | * is emitted. If not however, the expression for this value | ||
| 367 | * is replaced with another expression (normally a constructor) | ||
| 368 | * and the corresponding "setter" method is called to install | ||
| 369 | * the new property value in the object. This scheme removes | ||
| 370 | * default information from the output produced by streams | ||
| 371 | * using this delegate. | ||
| 372 | * <p> | ||
| 373 | * In passing these statements to the output stream, where they | ||
| 374 | * will be executed, side effects are made to the <code>newInstance</code>. | ||
| 375 | * In most cases this allows the problem of properties | ||
| 376 | * whose values depend on each other to actually help the | ||
| 377 | * serialization process by making the number of statements | ||
| 378 | * that need to be written to the output smaller. In general, | ||
| 379 | * the problem of handling interdependent properties is reduced to | ||
| 380 | * that of finding an order for the properties in | ||
| 381 | * a class such that no property value depends on the value of | ||
| 382 | * a subsequent property. | ||
| 383 | * | ||
| 384 | * @param oldInstance The instance to be copied. | ||
| 385 | * @param newInstance The instance that is to be modified. | ||
| 386 | * @param out The stream to which any initialization statements should be written. | ||
| 387 | * | ||
| 388 | * @throws NullPointerException if {@code out} is {@code null} | ||
| 389 | * | ||
| 390 | * @see java.beans.Introspector#getBeanInfo | ||
| 391 | * @see java.beans.PropertyDescriptor | ||
| 392 | */ | ||
| 393 | protected void initialize(Class<?> type, | ||
| 394 | Object oldInstance, Object newInstance, | ||
| 395 | Encoder out) | ||
| 396 | { | ||
| 397 | // System.out.println("DefulatPD:initialize" + type); | ||
| 398 | super.initialize(type, oldInstance, newInstance, out); | ||
| 399 | if (oldInstance.getClass() == type) { // !type.isInterface()) { | ||
| 400 | initBean(type, oldInstance, newInstance, out); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | private static PropertyDescriptor getPropertyDescriptor(Class type, String property) { | ||
| 405 | try { | ||
| 406 | for (PropertyDescriptor pd : Introspector.getBeanInfo(type).getPropertyDescriptors()) { | ||
| 407 | if (property.equals(pd.getName())) | ||
| 408 | return pd; | ||
| 409 | } | ||
| 410 | } catch (IntrospectionException exception) { | ||
| 411 | } | ||
| 412 | return null; | ||
| 413 | } | ||
| 414 | } | ||
Binary file not shown.
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * <p> | ||
| 30 | * This interface is intended to be implemented by, or delegated from, instances | ||
| 31 | * of java.beans.beancontext.BeanContext, in order to propagate to its nested hierarchy | ||
| 32 | * of java.beans.beancontext.BeanContextChild instances, the current "designTime" property. | ||
| 33 | * <p> | ||
| 34 | * The JavaBeans™ specification defines the notion of design time as is a | ||
| 35 | * mode in which JavaBeans instances should function during their composition | ||
| 36 | * and customization in a interactive design, composition or construction tool, | ||
| 37 | * as opposed to runtime when the JavaBean is part of an applet, application, | ||
| 38 | * or other live Java executable abstraction. | ||
| 39 | * | ||
| 40 | * @author Laurence P. G. Cable | ||
| 41 | * @since 1.2 | ||
| 42 | * | ||
| 43 | * @see java.beans.beancontext.BeanContext | ||
| 44 | * @see java.beans.beancontext.BeanContextChild | ||
| 45 | * @see java.beans.beancontext.BeanContextMembershipListener | ||
| 46 | * @see java.beans.PropertyChangeEvent | ||
| 47 | */ | ||
| 48 | |||
| 49 | public interface DesignMode { | ||
| 50 | |||
| 51 | /** | ||
| 52 | * The standard value of the propertyName as fired from a BeanContext or | ||
| 53 | * other source of PropertyChangeEvents. | ||
| 54 | */ | ||
| 55 | |||
| 56 | static String PROPERTYNAME = "designTime"; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Sets the "value" of the "designTime" property. | ||
| 60 | * <p> | ||
| 61 | * If the implementing object is an instance of java.beans.beancontext.BeanContext, | ||
| 62 | * or a subinterface thereof, then that BeanContext should fire a | ||
| 63 | * PropertyChangeEvent, to its registered BeanContextMembershipListeners, with | ||
| 64 | * parameters: | ||
| 65 | * <ul> | ||
| 66 | * <li><code>propertyName</code> - <code>java.beans.DesignMode.PROPERTYNAME</code> | ||
| 67 | * <li><code>oldValue</code> - previous value of "designTime" | ||
| 68 | * <li><code>newValue</code> - current value of "designTime" | ||
| 69 | * </ul> | ||
| 70 | * Note it is illegal for a BeanContextChild to invoke this method | ||
| 71 | * associated with a BeanContext that it is nested within. | ||
| 72 | * | ||
| 73 | * @param designTime the current "value" of the "designTime" property | ||
| 74 | * @see java.beans.beancontext.BeanContext | ||
| 75 | * @see java.beans.beancontext.BeanContextMembershipListener | ||
| 76 | * @see java.beans.PropertyChangeEvent | ||
| 77 | */ | ||
| 78 | |||
| 79 | void setDesignTime(boolean designTime); | ||
| 80 | |||
| 81 | /** | ||
| 82 | * A value of true denotes that JavaBeans should behave in design time | ||
| 83 | * mode, a value of false denotes runtime behavior. | ||
| 84 | * | ||
| 85 | * @return the current "value" of the "designTime" property. | ||
| 86 | */ | ||
| 87 | |||
| 88 | boolean isDesignTime(); | ||
| 89 | } | ||
Binary file not shown.
| @@ -0,0 +1,345 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import com.sun.beans.finder.PersistenceDelegateFinder; | ||
| 28 | |||
| 29 | import java.util.HashMap; | ||
| 30 | import java.util.IdentityHashMap; | ||
| 31 | import java.util.Map; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * An <code>Encoder</code> is a class which can be used to create | ||
| 35 | * files or streams that encode the state of a collection of | ||
| 36 | * JavaBeans in terms of their public APIs. The <code>Encoder</code>, | ||
| 37 | * in conjunction with its persistence delegates, is responsible for | ||
| 38 | * breaking the object graph down into a series of <code>Statements</code>s | ||
| 39 | * and <code>Expression</code>s which can be used to create it. | ||
| 40 | * A subclass typically provides a syntax for these expressions | ||
| 41 | * using some human readable form - like Java source code or XML. | ||
| 42 | * | ||
| 43 | * @since 1.4 | ||
| 44 | * | ||
| 45 | * @author Philip Milne | ||
| 46 | */ | ||
| 47 | |||
| 48 | public class Encoder { | ||
| 49 | private final PersistenceDelegateFinder finder = new PersistenceDelegateFinder(); | ||
| 50 | private Map bindings = new IdentityHashMap(); | ||
| 51 | private ExceptionListener exceptionListener; | ||
| 52 | boolean executeStatements = true; | ||
| 53 | private Map attributes; | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Write the specified object to the output stream. | ||
| 57 | * The serialized form will denote a series of | ||
| 58 | * expressions, the combined effect of which will create | ||
| 59 | * an equivalent object when the input stream is read. | ||
| 60 | * By default, the object is assumed to be a <em>JavaBean</em> | ||
| 61 | * with a nullary constructor, whose state is defined by | ||
| 62 | * the matching pairs of "setter" and "getter" methods | ||
| 63 | * returned by the Introspector. | ||
| 64 | * | ||
| 65 | * @param o The object to be written to the stream. | ||
| 66 | * | ||
| 67 | * @see XMLDecoder#readObject | ||
| 68 | */ | ||
| 69 | protected void writeObject(Object o) { | ||
| 70 | if (o == this) { | ||
| 71 | return; | ||
| 72 | } | ||
| 73 | PersistenceDelegate info = getPersistenceDelegate(o == null ? null : o.getClass()); | ||
| 74 | info.writeObject(o, this); | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Sets the exception handler for this stream to <code>exceptionListener</code>. | ||
| 79 | * The exception handler is notified when this stream catches recoverable | ||
| 80 | * exceptions. | ||
| 81 | * | ||
| 82 | * @param exceptionListener The exception handler for this stream; | ||
| 83 | * if <code>null</code> the default exception listener will be used. | ||
| 84 | * | ||
| 85 | * @see #getExceptionListener | ||
| 86 | */ | ||
| 87 | public void setExceptionListener(ExceptionListener exceptionListener) { | ||
| 88 | this.exceptionListener = exceptionListener; | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Gets the exception handler for this stream. | ||
| 93 | * | ||
| 94 | * @return The exception handler for this stream; | ||
| 95 | * Will return the default exception listener if this has not explicitly been set. | ||
| 96 | * | ||
| 97 | * @see #setExceptionListener | ||
| 98 | */ | ||
| 99 | public ExceptionListener getExceptionListener() { | ||
| 100 | return (exceptionListener != null) ? exceptionListener : Statement.defaultExceptionListener; | ||
| 101 | } | ||
| 102 | |||
| 103 | Object getValue(Expression exp) { | ||
| 104 | try { | ||
| 105 | return (exp == null) ? null : exp.getValue(); | ||
| 106 | } | ||
| 107 | catch (Exception e) { | ||
| 108 | getExceptionListener().exceptionThrown(e); | ||
| 109 | throw new RuntimeException("failed to evaluate: " + exp.toString()); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Returns the persistence delegate for the given type. | ||
| 115 | * The persistence delegate is calculated by applying | ||
| 116 | * the following rules in order: | ||
| 117 | * <ol> | ||
| 118 | * <li> | ||
| 119 | * If a persistence delegate is associated with the given type | ||
| 120 | * by using the {@link #setPersistenceDelegate} method | ||
| 121 | * it is returned. | ||
| 122 | * <li> | ||
| 123 | * A persistence delegate is then looked up by the name | ||
| 124 | * composed of the the fully qualified name of the given type | ||
| 125 | * and the "PersistenceDelegate" postfix. | ||
| 126 | * For example, a persistence delegate for the {@code Bean} class | ||
| 127 | * should be named {@code BeanPersistenceDelegate} | ||
| 128 | * and located in the same package. | ||
| 129 | * <pre> | ||
| 130 | * public class Bean { ... } | ||
| 131 | * public class BeanPersistenceDelegate { ... }</pre> | ||
| 132 | * The instance of the {@code BeanPersistenceDelegate} class | ||
| 133 | * is returned for the {@code Bean} class. | ||
| 134 | * <li> | ||
| 135 | * If the type is {@code null}, | ||
| 136 | * a shared internal persistence delegate is returned | ||
| 137 | * that encodes {@code null} value. | ||
| 138 | * <li> | ||
| 139 | * If the type is a {@code enum} declaration, | ||
| 140 | * a shared internal persistence delegate is returned | ||
| 141 | * that encodes constants of this enumeration | ||
| 142 | * by their names. | ||
| 143 | * <li> | ||
| 144 | * If the type is a primitive type or the corresponding wrapper, | ||
| 145 | * a shared internal persistence delegate is returned | ||
| 146 | * that encodes values of the given type. | ||
| 147 | * <li> | ||
| 148 | * If the type is an array, | ||
| 149 | * a shared internal persistence delegate is returned | ||
| 150 | * that encodes an array of the appropriate type and length, | ||
| 151 | * and each of its elements as if they are properties. | ||
| 152 | * <li> | ||
| 153 | * If the type is a proxy, | ||
| 154 | * a shared internal persistence delegate is returned | ||
| 155 | * that encodes a proxy instance by using | ||
| 156 | * the {@link java.lang.reflect.Proxy#newProxyInstance} method. | ||
| 157 | * <li> | ||
| 158 | * If the {@link BeanInfo} for this type has a {@link BeanDescriptor} | ||
| 159 | * which defined a "persistenceDelegate" attribute, | ||
| 160 | * the value of this named attribute is returned. | ||
| 161 | * <li> | ||
| 162 | * In all other cases the default persistence delegate is returned. | ||
| 163 | * The default persistence delegate assumes the type is a <em>JavaBean</em>, | ||
| 164 | * implying that it has a default constructor and that its state | ||
| 165 | * may be characterized by the matching pairs of "setter" and "getter" | ||
| 166 | * methods returned by the {@link Introspector} class. | ||
| 167 | * The default constructor is the constructor with the greatest number | ||
| 168 | * of parameters that has the {@link ConstructorProperties} annotation. | ||
| 169 | * If none of the constructors has the {@code ConstructorProperties} annotation, | ||
| 170 | * then the nullary constructor (constructor with no parameters) will be used. | ||
| 171 | * For example, in the following code fragment, the nullary constructor | ||
| 172 | * for the {@code Foo} class will be used, | ||
| 173 | * while the two-parameter constructor | ||
| 174 | * for the {@code Bar} class will be used. | ||
| 175 | * <pre> | ||
| 176 | * public class Foo { | ||
| 177 | * public Foo() { ... } | ||
| 178 | * public Foo(int x) { ... } | ||
| 179 | * } | ||
| 180 | * public class Bar { | ||
| 181 | * public Bar() { ... } | ||
| 182 | * @ConstructorProperties({"x"}) | ||
| 183 | * public Bar(int x) { ... } | ||
| 184 | * @ConstructorProperties({"x", "y"}) | ||
| 185 | * public Bar(int x, int y) { ... } | ||
| 186 | * }</pre> | ||
| 187 | * </ol> | ||
| 188 | * | ||
| 189 | * @param type the class of the objects | ||
| 190 | * @return the persistence delegate for the given type | ||
| 191 | * | ||
| 192 | * @see #setPersistenceDelegate | ||
| 193 | * @see java.beans.Introspector#getBeanInfo | ||
| 194 | * @see java.beans.BeanInfo#getBeanDescriptor | ||
| 195 | */ | ||
| 196 | public PersistenceDelegate getPersistenceDelegate(Class<?> type) { | ||
| 197 | PersistenceDelegate pd = this.finder.find(type); | ||
| 198 | return (pd != null) ? pd : MetaData.getPersistenceDelegate(type); | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * Associates the specified persistence delegate with the given type. | ||
| 203 | * | ||
| 204 | * @param type the class of objects that the specified persistence delegate applies to | ||
| 205 | * @param delegate the persistence delegate for instances of the given type | ||
| 206 | * | ||
| 207 | * @see #getPersistenceDelegate | ||
| 208 | * @see java.beans.Introspector#getBeanInfo | ||
| 209 | * @see java.beans.BeanInfo#getBeanDescriptor | ||
| 210 | */ | ||
| 211 | public void setPersistenceDelegate(Class<?> type, PersistenceDelegate delegate) { | ||
| 212 | this.finder.register(type, delegate); | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * Removes the entry for this instance, returning the old entry. | ||
| 217 | * | ||
| 218 | * @param oldInstance The entry that should be removed. | ||
| 219 | * @return The entry that was removed. | ||
| 220 | * | ||
| 221 | * @see #get | ||
| 222 | */ | ||
| 223 | public Object remove(Object oldInstance) { | ||
| 224 | Expression exp = (Expression)bindings.remove(oldInstance); | ||
| 225 | return getValue(exp); | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Returns a tentative value for <code>oldInstance</code> in | ||
| 230 | * the environment created by this stream. A persistence | ||
| 231 | * delegate can use its <code>mutatesTo</code> method to | ||
| 232 | * determine whether this value may be initialized to | ||
| 233 | * form the equivalent object at the output or whether | ||
| 234 | * a new object must be instantiated afresh. If the | ||
| 235 | * stream has not yet seen this value, null is returned. | ||
| 236 | * | ||
| 237 | * @param oldInstance The instance to be looked up. | ||
| 238 | * @return The object, null if the object has not been seen before. | ||
| 239 | */ | ||
| 240 | public Object get(Object oldInstance) { | ||
| 241 | if (oldInstance == null || oldInstance == this || | ||
| 242 | oldInstance.getClass() == String.class) { | ||
| 243 | return oldInstance; | ||
| 244 | } | ||
| 245 | Expression exp = (Expression)bindings.get(oldInstance); | ||
| 246 | return getValue(exp); | ||
| 247 | } | ||
| 248 | |||
| 249 | private Object writeObject1(Object oldInstance) { | ||
| 250 | Object o = get(oldInstance); | ||
| 251 | if (o == null) { | ||
| 252 | writeObject(oldInstance); | ||
| 253 | o = get(oldInstance); | ||
| 254 | } | ||
| 255 | return o; | ||
| 256 | } | ||
| 257 | |||
| 258 | private Statement cloneStatement(Statement oldExp) { | ||
| 259 | Object oldTarget = oldExp.getTarget(); | ||
| 260 | Object newTarget = writeObject1(oldTarget); | ||
| 261 | |||
| 262 | Object[] oldArgs = oldExp.getArguments(); | ||
| 263 | Object[] newArgs = new Object[oldArgs.length]; | ||
| 264 | for (int i = 0; i < oldArgs.length; i++) { | ||
| 265 | newArgs[i] = writeObject1(oldArgs[i]); | ||
| 266 | } | ||
| 267 | Statement newExp = Statement.class.equals(oldExp.getClass()) | ||
| 268 | ? new Statement(newTarget, oldExp.getMethodName(), newArgs) | ||
| 269 | : new Expression(newTarget, oldExp.getMethodName(), newArgs); | ||
| 270 | newExp.loader = oldExp.loader; | ||
| 271 | return newExp; | ||
| 272 | } | ||
| 273 | |||
| 274 | /** | ||
| 275 | * Writes statement <code>oldStm</code> to the stream. | ||
| 276 | * The <code>oldStm</code> should be written entirely | ||
| 277 | * in terms of the callers environment, i.e. the | ||
| 278 | * target and all arguments should be part of the | ||
| 279 | * object graph being written. These expressions | ||
| 280 | * represent a series of "what happened" expressions | ||
| 281 | * which tell the output stream how to produce an | ||
| 282 | * object graph like the original. | ||
| 283 | * <p> | ||
| 284 | * The implementation of this method will produce | ||
| 285 | * a second expression to represent the same expression in | ||
| 286 | * an environment that will exist when the stream is read. | ||
| 287 | * This is achieved simply by calling <code>writeObject</code> | ||
| 288 | * on the target and all the arguments and building a new | ||
| 289 | * expression with the results. | ||
| 290 | * | ||
| 291 | * @param oldStm The expression to be written to the stream. | ||
| 292 | */ | ||
| 293 | public void writeStatement(Statement oldStm) { | ||
| 294 | // System.out.println("writeStatement: " + oldExp); | ||
| 295 | Statement newStm = cloneStatement(oldStm); | ||
| 296 | if (oldStm.getTarget() != this && executeStatements) { | ||
| 297 | try { | ||
| 298 | newStm.execute(); | ||
| 299 | } catch (Exception e) { | ||
| 300 | getExceptionListener().exceptionThrown(new Exception("Encoder: discarding statement " | ||
| 301 | + newStm, e)); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | /** | ||
| 307 | * The implementation first checks to see if an | ||
| 308 | * expression with this value has already been written. | ||
| 309 | * If not, the expression is cloned, using | ||
| 310 | * the same procedure as <code>writeStatement</code>, | ||
| 311 | * and the value of this expression is reconciled | ||
| 312 | * with the value of the cloned expression | ||
| 313 | * by calling <code>writeObject</code>. | ||
| 314 | * | ||
| 315 | * @param oldExp The expression to be written to the stream. | ||
| 316 | */ | ||
| 317 | public void writeExpression(Expression oldExp) { | ||
| 318 | // System.out.println("Encoder::writeExpression: " + oldExp); | ||
| 319 | Object oldValue = getValue(oldExp); | ||
| 320 | if (get(oldValue) != null) { | ||
| 321 | return; | ||
| 322 | } | ||
| 323 | bindings.put(oldValue, (Expression)cloneStatement(oldExp)); | ||
| 324 | writeObject(oldValue); | ||
| 325 | } | ||
| 326 | |||
| 327 | void clear() { | ||
| 328 | bindings.clear(); | ||
| 329 | } | ||
| 330 | |||
| 331 | // Package private method for setting an attributes table for the encoder | ||
| 332 | void setAttribute(Object key, Object value) { | ||
| 333 | if (attributes == null) { | ||
| 334 | attributes = new HashMap(); | ||
| 335 | } | ||
| 336 | attributes.put(key, value); | ||
| 337 | } | ||
| 338 | |||
| 339 | Object getAttribute(Object key) { | ||
| 340 | if (attributes == null) { | ||
| 341 | return null; | ||
| 342 | } | ||
| 343 | return attributes.get(key); | ||
| 344 | } | ||
| 345 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,694 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.lang.reflect.InvocationHandler; | ||
| 28 | import java.lang.reflect.InvocationTargetException; | ||
| 29 | import java.lang.reflect.Proxy; | ||
| 30 | import java.lang.reflect.Method; | ||
| 31 | import java.security.AccessControlContext; | ||
| 32 | import java.security.AccessController; | ||
| 33 | import java.security.PrivilegedAction; | ||
| 34 | |||
| 35 | import sun.reflect.misc.MethodUtil; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * The <code>EventHandler</code> class provides | ||
| 39 | * support for dynamically generating event listeners whose methods | ||
| 40 | * execute a simple statement involving an incoming event object | ||
| 41 | * and a target object. | ||
| 42 | * <p> | ||
| 43 | * The <code>EventHandler</code> class is intended to be used by interactive tools, such as | ||
| 44 | * application builders, that allow developers to make connections between | ||
| 45 | * beans. Typically connections are made from a user interface bean | ||
| 46 | * (the event <em>source</em>) | ||
| 47 | * to an application logic bean (the <em>target</em>). The most effective | ||
| 48 | * connections of this kind isolate the application logic from the user | ||
| 49 | * interface. For example, the <code>EventHandler</code> for a | ||
| 50 | * connection from a <code>JCheckBox</code> to a method | ||
| 51 | * that accepts a boolean value can deal with extracting the state | ||
| 52 | * of the check box and passing it directly to the method so that | ||
| 53 | * the method is isolated from the user interface layer. | ||
| 54 | * <p> | ||
| 55 | * Inner classes are another, more general way to handle events from | ||
| 56 | * user interfaces. The <code>EventHandler</code> class | ||
| 57 | * handles only a subset of what is possible using inner | ||
| 58 | * classes. However, <code>EventHandler</code> works better | ||
| 59 | * with the long-term persistence scheme than inner classes. | ||
| 60 | * Also, using <code>EventHandler</code> in large applications in | ||
| 61 | * which the same interface is implemented many times can | ||
| 62 | * reduce the disk and memory footprint of the application. | ||
| 63 | * <p> | ||
| 64 | * The reason that listeners created with <code>EventHandler</code> | ||
| 65 | * have such a small | ||
| 66 | * footprint is that the <code>Proxy</code> class, on which | ||
| 67 | * the <code>EventHandler</code> relies, shares implementations | ||
| 68 | * of identical | ||
| 69 | * interfaces. For example, if you use | ||
| 70 | * the <code>EventHandler</code> <code>create</code> methods to make | ||
| 71 | * all the <code>ActionListener</code>s in an application, | ||
| 72 | * all the action listeners will be instances of a single class | ||
| 73 | * (one created by the <code>Proxy</code> class). | ||
| 74 | * In general, listeners based on | ||
| 75 | * the <code>Proxy</code> class require one listener class | ||
| 76 | * to be created per <em>listener type</em> (interface), | ||
| 77 | * whereas the inner class | ||
| 78 | * approach requires one class to be created per <em>listener</em> | ||
| 79 | * (object that implements the interface). | ||
| 80 | * | ||
| 81 | * <p> | ||
| 82 | * You don't generally deal directly with <code>EventHandler</code> | ||
| 83 | * instances. | ||
| 84 | * Instead, you use one of the <code>EventHandler</code> | ||
| 85 | * <code>create</code> methods to create | ||
| 86 | * an object that implements a given listener interface. | ||
| 87 | * This listener object uses an <code>EventHandler</code> object | ||
| 88 | * behind the scenes to encapsulate information about the | ||
| 89 | * event, the object to be sent a message when the event occurs, | ||
| 90 | * the message (method) to be sent, and any argument | ||
| 91 | * to the method. | ||
| 92 | * The following section gives examples of how to create listener | ||
| 93 | * objects using the <code>create</code> methods. | ||
| 94 | * | ||
| 95 | * <h2>Examples of Using EventHandler</h2> | ||
| 96 | * | ||
| 97 | * The simplest use of <code>EventHandler</code> is to install | ||
| 98 | * a listener that calls a method on the target object with no arguments. | ||
| 99 | * In the following example we create an <code>ActionListener</code> | ||
| 100 | * that invokes the <code>toFront</code> method on an instance | ||
| 101 | * of <code>javax.swing.JFrame</code>. | ||
| 102 | * | ||
| 103 | * <blockquote> | ||
| 104 | *<pre> | ||
| 105 | *myButton.addActionListener( | ||
| 106 | * (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront")); | ||
| 107 | *</pre> | ||
| 108 | * </blockquote> | ||
| 109 | * | ||
| 110 | * When <code>myButton</code> is pressed, the statement | ||
| 111 | * <code>frame.toFront()</code> will be executed. One could get | ||
| 112 | * the same effect, with some additional compile-time type safety, | ||
| 113 | * by defining a new implementation of the <code>ActionListener</code> | ||
| 114 | * interface and adding an instance of it to the button: | ||
| 115 | * | ||
| 116 | * <blockquote> | ||
| 117 | *<pre> | ||
| 118 | //Equivalent code using an inner class instead of EventHandler. | ||
| 119 | *myButton.addActionListener(new ActionListener() { | ||
| 120 | * public void actionPerformed(ActionEvent e) { | ||
| 121 | * frame.toFront(); | ||
| 122 | * } | ||
| 123 | *}); | ||
| 124 | *</pre> | ||
| 125 | * </blockquote> | ||
| 126 | * | ||
| 127 | * The next simplest use of <code>EventHandler</code> is | ||
| 128 | * to extract a property value from the first argument | ||
| 129 | * of the method in the listener interface (typically an event object) | ||
| 130 | * and use it to set the value of a property in the target object. | ||
| 131 | * In the following example we create an <code>ActionListener</code> that | ||
| 132 | * sets the <code>nextFocusableComponent</code> property of the target | ||
| 133 | * (myButton) object to the value of the "source" property of the event. | ||
| 134 | * | ||
| 135 | * <blockquote> | ||
| 136 | *<pre> | ||
| 137 | *EventHandler.create(ActionListener.class, myButton, "nextFocusableComponent", "source") | ||
| 138 | *</pre> | ||
| 139 | * </blockquote> | ||
| 140 | * | ||
| 141 | * This would correspond to the following inner class implementation: | ||
| 142 | * | ||
| 143 | * <blockquote> | ||
| 144 | *<pre> | ||
| 145 | //Equivalent code using an inner class instead of EventHandler. | ||
| 146 | *new ActionListener() { | ||
| 147 | * public void actionPerformed(ActionEvent e) { | ||
| 148 | * myButton.setNextFocusableComponent((Component)e.getSource()); | ||
| 149 | * } | ||
| 150 | *} | ||
| 151 | *</pre> | ||
| 152 | * </blockquote> | ||
| 153 | * | ||
| 154 | * It's also possible to create an <code>EventHandler</code> that | ||
| 155 | * just passes the incoming event object to the target's action. | ||
| 156 | * If the fourth <code>EventHandler.create</code> argument is | ||
| 157 | * an empty string, then the event is just passed along: | ||
| 158 | * | ||
| 159 | * <blockquote> | ||
| 160 | *<pre> | ||
| 161 | *EventHandler.create(ActionListener.class, target, "doActionEvent", "") | ||
| 162 | *</pre> | ||
| 163 | * </blockquote> | ||
| 164 | * | ||
| 165 | * This would correspond to the following inner class implementation: | ||
| 166 | * | ||
| 167 | * <blockquote> | ||
| 168 | *<pre> | ||
| 169 | //Equivalent code using an inner class instead of EventHandler. | ||
| 170 | *new ActionListener() { | ||
| 171 | * public void actionPerformed(ActionEvent e) { | ||
| 172 | * target.doActionEvent(e); | ||
| 173 | * } | ||
| 174 | *} | ||
| 175 | *</pre> | ||
| 176 | * </blockquote> | ||
| 177 | * | ||
| 178 | * Probably the most common use of <code>EventHandler</code> | ||
| 179 | * is to extract a property value from the | ||
| 180 | * <em>source</em> of the event object and set this value as | ||
| 181 | * the value of a property of the target object. | ||
| 182 | * In the following example we create an <code>ActionListener</code> that | ||
| 183 | * sets the "label" property of the target | ||
| 184 | * object to the value of the "text" property of the | ||
| 185 | * source (the value of the "source" property) of the event. | ||
| 186 | * | ||
| 187 | * <blockquote> | ||
| 188 | *<pre> | ||
| 189 | *EventHandler.create(ActionListener.class, myButton, "label", "source.text") | ||
| 190 | *</pre> | ||
| 191 | * </blockquote> | ||
| 192 | * | ||
| 193 | * This would correspond to the following inner class implementation: | ||
| 194 | * | ||
| 195 | * <blockquote> | ||
| 196 | *<pre> | ||
| 197 | //Equivalent code using an inner class instead of EventHandler. | ||
| 198 | *new ActionListener { | ||
| 199 | * public void actionPerformed(ActionEvent e) { | ||
| 200 | * myButton.setLabel(((JTextField)e.getSource()).getText()); | ||
| 201 | * } | ||
| 202 | *} | ||
| 203 | *</pre> | ||
| 204 | * </blockquote> | ||
| 205 | * | ||
| 206 | * The event property may be "qualified" with an arbitrary number | ||
| 207 | * of property prefixes delimited with the "." character. The "qualifying" | ||
| 208 | * names that appear before the "." characters are taken as the names of | ||
| 209 | * properties that should be applied, left-most first, to | ||
| 210 | * the event object. | ||
| 211 | * <p> | ||
| 212 | * For example, the following action listener | ||
| 213 | * | ||
| 214 | * <blockquote> | ||
| 215 | *<pre> | ||
| 216 | *EventHandler.create(ActionListener.class, target, "a", "b.c.d") | ||
| 217 | *</pre> | ||
| 218 | * </blockquote> | ||
| 219 | * | ||
| 220 | * might be written as the following inner class | ||
| 221 | * (assuming all the properties had canonical getter methods and | ||
| 222 | * returned the appropriate types): | ||
| 223 | * | ||
| 224 | * <blockquote> | ||
| 225 | *<pre> | ||
| 226 | //Equivalent code using an inner class instead of EventHandler. | ||
| 227 | *new ActionListener { | ||
| 228 | * public void actionPerformed(ActionEvent e) { | ||
| 229 | * target.setA(e.getB().getC().isD()); | ||
| 230 | * } | ||
| 231 | *} | ||
| 232 | *</pre> | ||
| 233 | * </blockquote> | ||
| 234 | * The target property may also be "qualified" with an arbitrary number | ||
| 235 | * of property prefixs delimited with the "." character. For example, the | ||
| 236 | * following action listener: | ||
| 237 | * <pre> | ||
| 238 | * EventHandler.create(ActionListener.class, target, "a.b", "c.d") | ||
| 239 | * </pre> | ||
| 240 | * might be written as the following inner class | ||
| 241 | * (assuming all the properties had canonical getter methods and | ||
| 242 | * returned the appropriate types): | ||
| 243 | * <pre> | ||
| 244 | * //Equivalent code using an inner class instead of EventHandler. | ||
| 245 | * new ActionListener { | ||
| 246 | * public void actionPerformed(ActionEvent e) { | ||
| 247 | * target.getA().setB(e.getC().isD()); | ||
| 248 | * } | ||
| 249 | *} | ||
| 250 | *</pre> | ||
| 251 | * <p> | ||
| 252 | * As <code>EventHandler</code> ultimately relies on reflection to invoke | ||
| 253 | * a method we recommend against targeting an overloaded method. For example, | ||
| 254 | * if the target is an instance of the class <code>MyTarget</code> which is | ||
| 255 | * defined as: | ||
| 256 | * <pre> | ||
| 257 | * public class MyTarget { | ||
| 258 | * public void doIt(String); | ||
| 259 | * public void doIt(Object); | ||
| 260 | * } | ||
| 261 | * </pre> | ||
| 262 | * Then the method <code>doIt</code> is overloaded. EventHandler will invoke | ||
| 263 | * the method that is appropriate based on the source. If the source is | ||
| 264 | * null, then either method is appropriate and the one that is invoked is | ||
| 265 | * undefined. For that reason we recommend against targeting overloaded | ||
| 266 | * methods. | ||
| 267 | * | ||
| 268 | * @see java.lang.reflect.Proxy | ||
| 269 | * @see java.util.EventObject | ||
| 270 | * | ||
| 271 | * @since 1.4 | ||
| 272 | * | ||
| 273 | * @author Mark Davidson | ||
| 274 | * @author Philip Milne | ||
| 275 | * @author Hans Muller | ||
| 276 | * | ||
| 277 | */ | ||
| 278 | public class EventHandler implements InvocationHandler { | ||
| 279 | private Object target; | ||
| 280 | private String action; | ||
| 281 | private final String eventPropertyName; | ||
| 282 | private final String listenerMethodName; | ||
| 283 | private final AccessControlContext acc = AccessController.getContext(); | ||
| 284 | |||
| 285 | /** | ||
| 286 | * Creates a new <code>EventHandler</code> object; | ||
| 287 | * you generally use one of the <code>create</code> methods | ||
| 288 | * instead of invoking this constructor directly. Refer to | ||
| 289 | * {@link java.beans.EventHandler#create(Class, Object, String, String) | ||
| 290 | * the general version of create} for a complete description of | ||
| 291 | * the <code>eventPropertyName</code> and <code>listenerMethodName</code> | ||
| 292 | * parameter. | ||
| 293 | * | ||
| 294 | * @param target the object that will perform the action | ||
| 295 | * @param action the name of a (possibly qualified) property or method on | ||
| 296 | * the target | ||
| 297 | * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event | ||
| 298 | * @param listenerMethodName the name of the method in the listener interface that should trigger the action | ||
| 299 | * | ||
| 300 | * @throws NullPointerException if <code>target</code> is null | ||
| 301 | * @throws NullPointerException if <code>action</code> is null | ||
| 302 | * | ||
| 303 | * @see EventHandler | ||
| 304 | * @see #create(Class, Object, String, String, String) | ||
| 305 | * @see #getTarget | ||
| 306 | * @see #getAction | ||
| 307 | * @see #getEventPropertyName | ||
| 308 | * @see #getListenerMethodName | ||
| 309 | */ | ||
| 310 | @ConstructorProperties({"target", "action", "eventPropertyName", "listenerMethodName"}) | ||
| 311 | public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) { | ||
| 312 | this.target = target; | ||
| 313 | this.action = action; | ||
| 314 | if (target == null) { | ||
| 315 | throw new NullPointerException("target must be non-null"); | ||
| 316 | } | ||
| 317 | if (action == null) { | ||
| 318 | throw new NullPointerException("action must be non-null"); | ||
| 319 | } | ||
| 320 | this.eventPropertyName = eventPropertyName; | ||
| 321 | this.listenerMethodName = listenerMethodName; | ||
| 322 | } | ||
| 323 | |||
| 324 | /** | ||
| 325 | * Returns the object to which this event handler will send a message. | ||
| 326 | * | ||
| 327 | * @return the target of this event handler | ||
| 328 | * @see #EventHandler(Object, String, String, String) | ||
| 329 | */ | ||
| 330 | public Object getTarget() { | ||
| 331 | return target; | ||
| 332 | } | ||
| 333 | |||
| 334 | /** | ||
| 335 | * Returns the name of the target's writable property | ||
| 336 | * that this event handler will set, | ||
| 337 | * or the name of the method that this event handler | ||
| 338 | * will invoke on the target. | ||
| 339 | * | ||
| 340 | * @return the action of this event handler | ||
| 341 | * @see #EventHandler(Object, String, String, String) | ||
| 342 | */ | ||
| 343 | public String getAction() { | ||
| 344 | return action; | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * Returns the property of the event that should be | ||
| 349 | * used in the action applied to the target. | ||
| 350 | * | ||
| 351 | * @return the property of the event | ||
| 352 | * | ||
| 353 | * @see #EventHandler(Object, String, String, String) | ||
| 354 | */ | ||
| 355 | public String getEventPropertyName() { | ||
| 356 | return eventPropertyName; | ||
| 357 | } | ||
| 358 | |||
| 359 | /** | ||
| 360 | * Returns the name of the method that will trigger the action. | ||
| 361 | * A return value of <code>null</code> signifies that all methods in the | ||
| 362 | * listener interface trigger the action. | ||
| 363 | * | ||
| 364 | * @return the name of the method that will trigger the action | ||
| 365 | * | ||
| 366 | * @see #EventHandler(Object, String, String, String) | ||
| 367 | */ | ||
| 368 | public String getListenerMethodName() { | ||
| 369 | return listenerMethodName; | ||
| 370 | } | ||
| 371 | |||
| 372 | private Object applyGetters(Object target, String getters) { | ||
| 373 | if (getters == null || getters.equals("")) { | ||
| 374 | return target; | ||
| 375 | } | ||
| 376 | int firstDot = getters.indexOf('.'); | ||
| 377 | if (firstDot == -1) { | ||
| 378 | firstDot = getters.length(); | ||
| 379 | } | ||
| 380 | String first = getters.substring(0, firstDot); | ||
| 381 | String rest = getters.substring(Math.min(firstDot + 1, getters.length())); | ||
| 382 | |||
| 383 | try { | ||
| 384 | Method getter = null; | ||
| 385 | if (target != null) { | ||
| 386 | getter = Statement.getMethod(target.getClass(), | ||
| 387 | "get" + NameGenerator.capitalize(first), | ||
| 388 | new Class[]{}); | ||
| 389 | if (getter == null) { | ||
| 390 | getter = Statement.getMethod(target.getClass(), | ||
| 391 | "is" + NameGenerator.capitalize(first), | ||
| 392 | new Class[]{}); | ||
| 393 | } | ||
| 394 | if (getter == null) { | ||
| 395 | getter = Statement.getMethod(target.getClass(), first, new Class[]{}); | ||
| 396 | } | ||
| 397 | } | ||
| 398 | if (getter == null) { | ||
| 399 | throw new RuntimeException("No method called: " + first + | ||
| 400 | " defined on " + target); | ||
| 401 | } | ||
| 402 | Object newTarget = MethodUtil.invoke(getter, target, new Object[]{}); | ||
| 403 | return applyGetters(newTarget, rest); | ||
| 404 | } | ||
| 405 | catch (Exception e) { | ||
| 406 | throw new RuntimeException("Failed to call method: " + first + | ||
| 407 | " on " + target, e); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | /** | ||
| 412 | * Extract the appropriate property value from the event and | ||
| 413 | * pass it to the action associated with | ||
| 414 | * this <code>EventHandler</code>. | ||
| 415 | * | ||
| 416 | * @param proxy the proxy object | ||
| 417 | * @param method the method in the listener interface | ||
| 418 | * @return the result of applying the action to the target | ||
| 419 | * | ||
| 420 | * @see EventHandler | ||
| 421 | */ | ||
| 422 | public Object invoke(final Object proxy, final Method method, final Object[] arguments) { | ||
| 423 | AccessControlContext acc = this.acc; | ||
| 424 | if ((acc == null) && (System.getSecurityManager() != null)) { | ||
| 425 | throw new SecurityException("AccessControlContext is not set"); | ||
| 426 | } | ||
| 427 | return AccessController.doPrivileged(new PrivilegedAction<Object>() { | ||
| 428 | public Object run() { | ||
| 429 | return invokeInternal(proxy, method, arguments); | ||
| 430 | } | ||
| 431 | }, acc); | ||
| 432 | } | ||
| 433 | |||
| 434 | private Object invokeInternal(Object proxy, Method method, Object[] arguments) { | ||
| 435 | String methodName = method.getName(); | ||
| 436 | if (method.getDeclaringClass() == Object.class) { | ||
| 437 | // Handle the Object public methods. | ||
| 438 | if (methodName.equals("hashCode")) { | ||
| 439 | return new Integer(System.identityHashCode(proxy)); | ||
| 440 | } else if (methodName.equals("equals")) { | ||
| 441 | return (proxy == arguments[0] ? Boolean.TRUE : Boolean.FALSE); | ||
| 442 | } else if (methodName.equals("toString")) { | ||
| 443 | return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 447 | if (listenerMethodName == null || listenerMethodName.equals(methodName)) { | ||
| 448 | Class[] argTypes = null; | ||
| 449 | Object[] newArgs = null; | ||
| 450 | |||
| 451 | if (eventPropertyName == null) { // Nullary method. | ||
| 452 | newArgs = new Object[]{}; | ||
| 453 | argTypes = new Class[]{}; | ||
| 454 | } | ||
| 455 | else { | ||
| 456 | Object input = applyGetters(arguments[0], getEventPropertyName()); | ||
| 457 | newArgs = new Object[]{input}; | ||
| 458 | argTypes = new Class[]{input == null ? null : | ||
| 459 | input.getClass()}; | ||
| 460 | } | ||
| 461 | try { | ||
| 462 | int lastDot = action.lastIndexOf('.'); | ||
| 463 | if (lastDot != -1) { | ||
| 464 | target = applyGetters(target, action.substring(0, lastDot)); | ||
| 465 | action = action.substring(lastDot + 1); | ||
| 466 | } | ||
| 467 | Method targetMethod = Statement.getMethod( | ||
| 468 | target.getClass(), action, argTypes); | ||
| 469 | if (targetMethod == null) { | ||
| 470 | targetMethod = Statement.getMethod(target.getClass(), | ||
| 471 | "set" + NameGenerator.capitalize(action), argTypes); | ||
| 472 | } | ||
| 473 | if (targetMethod == null) { | ||
| 474 | String argTypeString = (argTypes.length == 0) | ||
| 475 | ? " with no arguments" | ||
| 476 | : " with argument " + argTypes[0]; | ||
| 477 | throw new RuntimeException( | ||
| 478 | "No method called " + action + " on " + | ||
| 479 | target.getClass() + argTypeString); | ||
| 480 | } | ||
| 481 | return MethodUtil.invoke(targetMethod, target, newArgs); | ||
| 482 | } | ||
| 483 | catch (IllegalAccessException ex) { | ||
| 484 | throw new RuntimeException(ex); | ||
| 485 | } | ||
| 486 | catch (InvocationTargetException ex) { | ||
| 487 | Throwable th = ex.getTargetException(); | ||
| 488 | throw (th instanceof RuntimeException) | ||
| 489 | ? (RuntimeException) th | ||
| 490 | : new RuntimeException(th); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | return null; | ||
| 494 | } | ||
| 495 | |||
| 496 | /** | ||
| 497 | * Creates an implementation of <code>listenerInterface</code> in which | ||
| 498 | * <em>all</em> of the methods in the listener interface apply | ||
| 499 | * the handler's <code>action</code> to the <code>target</code>. This | ||
| 500 | * method is implemented by calling the other, more general, | ||
| 501 | * implementation of the <code>create</code> method with both | ||
| 502 | * the <code>eventPropertyName</code> and the <code>listenerMethodName</code> | ||
| 503 | * taking the value <code>null</code>. Refer to | ||
| 504 | * {@link java.beans.EventHandler#create(Class, Object, String, String) | ||
| 505 | * the general version of create} for a complete description of | ||
| 506 | * the <code>action</code> parameter. | ||
| 507 | * <p> | ||
| 508 | * To create an <code>ActionListener</code> that shows a | ||
| 509 | * <code>JDialog</code> with <code>dialog.show()</code>, | ||
| 510 | * one can write: | ||
| 511 | * | ||
| 512 | *<blockquote> | ||
| 513 | *<pre> | ||
| 514 | *EventHandler.create(ActionListener.class, dialog, "show") | ||
| 515 | *</pre> | ||
| 516 | *</blockquote> | ||
| 517 | * | ||
| 518 | * @param listenerInterface the listener interface to create a proxy for | ||
| 519 | * @param target the object that will perform the action | ||
| 520 | * @param action the name of a (possibly qualified) property or method on | ||
| 521 | * the target | ||
| 522 | * @return an object that implements <code>listenerInterface</code> | ||
| 523 | * | ||
| 524 | * @throws NullPointerException if <code>listenerInterface</code> is null | ||
| 525 | * @throws NullPointerException if <code>target</code> is null | ||
| 526 | * @throws NullPointerException if <code>action</code> is null | ||
| 527 | * | ||
| 528 | * @see #create(Class, Object, String, String) | ||
| 529 | */ | ||
| 530 | public static <T> T create(Class<T> listenerInterface, | ||
| 531 | Object target, String action) | ||
| 532 | { | ||
| 533 | return create(listenerInterface, target, action, null, null); | ||
| 534 | } | ||
| 535 | |||
| 536 | /** | ||
| 537 | /** | ||
| 538 | * Creates an implementation of <code>listenerInterface</code> in which | ||
| 539 | * <em>all</em> of the methods pass the value of the event | ||
| 540 | * expression, <code>eventPropertyName</code>, to the final method in the | ||
| 541 | * statement, <code>action</code>, which is applied to the <code>target</code>. | ||
| 542 | * This method is implemented by calling the | ||
| 543 | * more general, implementation of the <code>create</code> method with | ||
| 544 | * the <code>listenerMethodName</code> taking the value <code>null</code>. | ||
| 545 | * Refer to | ||
| 546 | * {@link java.beans.EventHandler#create(Class, Object, String, String) | ||
| 547 | * the general version of create} for a complete description of | ||
| 548 | * the <code>action</code> and <code>eventPropertyName</code> parameters. | ||
| 549 | * <p> | ||
| 550 | * To create an <code>ActionListener</code> that sets the | ||
| 551 | * the text of a <code>JLabel</code> to the text value of | ||
| 552 | * the <code>JTextField</code> source of the incoming event, | ||
| 553 | * you can use the following code: | ||
| 554 | * | ||
| 555 | *<blockquote> | ||
| 556 | *<pre> | ||
| 557 | *EventHandler.create(ActionListener.class, label, "text", "source.text"); | ||
| 558 | *</pre> | ||
| 559 | *</blockquote> | ||
| 560 | * | ||
| 561 | * This is equivalent to the following code: | ||
| 562 | *<blockquote> | ||
| 563 | *<pre> | ||
| 564 | //Equivalent code using an inner class instead of EventHandler. | ||
| 565 | *new ActionListener() { | ||
| 566 | * public void actionPerformed(ActionEvent event) { | ||
| 567 | * label.setText(((JTextField)(event.getSource())).getText()); | ||
| 568 | * } | ||
| 569 | *}; | ||
| 570 | *</pre> | ||
| 571 | *</blockquote> | ||
| 572 | * | ||
| 573 | * @param listenerInterface the listener interface to create a proxy for | ||
| 574 | * @param target the object that will perform the action | ||
| 575 | * @param action the name of a (possibly qualified) property or method on | ||
| 576 | * the target | ||
| 577 | * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event | ||
| 578 | * | ||
| 579 | * @return an object that implements <code>listenerInterface</code> | ||
| 580 | * | ||
| 581 | * @throws NullPointerException if <code>listenerInterface</code> is null | ||
| 582 | * @throws NullPointerException if <code>target</code> is null | ||
| 583 | * @throws NullPointerException if <code>action</code> is null | ||
| 584 | * | ||
| 585 | * @see #create(Class, Object, String, String, String) | ||
| 586 | */ | ||
| 587 | public static <T> T create(Class<T> listenerInterface, | ||
| 588 | Object target, String action, | ||
| 589 | String eventPropertyName) | ||
| 590 | { | ||
| 591 | return create(listenerInterface, target, action, eventPropertyName, null); | ||
| 592 | } | ||
| 593 | |||
| 594 | /** | ||
| 595 | * Creates an implementation of <code>listenerInterface</code> in which | ||
| 596 | * the method named <code>listenerMethodName</code> | ||
| 597 | * passes the value of the event expression, <code>eventPropertyName</code>, | ||
| 598 | * to the final method in the statement, <code>action</code>, which | ||
| 599 | * is applied to the <code>target</code>. All of the other listener | ||
| 600 | * methods do nothing. | ||
| 601 | * <p> | ||
| 602 | * The <code>eventPropertyName</code> string is used to extract a value | ||
| 603 | * from the incoming event object that is passed to the target | ||
| 604 | * method. The common case is the target method takes no arguments, in | ||
| 605 | * which case a value of null should be used for the | ||
| 606 | * <code>eventPropertyName</code>. Alternatively if you want | ||
| 607 | * the incoming event object passed directly to the target method use | ||
| 608 | * the empty string. | ||
| 609 | * The format of the <code>eventPropertyName</code> string is a sequence of | ||
| 610 | * methods or properties where each method or | ||
| 611 | * property is applied to the value returned by the preceeding method | ||
| 612 | * starting from the incoming event object. | ||
| 613 | * The syntax is: <code>propertyName{.propertyName}*</code> | ||
| 614 | * where <code>propertyName</code> matches a method or | ||
| 615 | * property. For example, to extract the <code>point</code> | ||
| 616 | * property from a <code>MouseEvent</code>, you could use either | ||
| 617 | * <code>"point"</code> or <code>"getPoint"</code> as the | ||
| 618 | * <code>eventPropertyName</code>. To extract the "text" property from | ||
| 619 | * a <code>MouseEvent</code> with a <code>JLabel</code> source use any | ||
| 620 | * of the following as <code>eventPropertyName</code>: | ||
| 621 | * <code>"source.text"</code>, | ||
| 622 | * <code>"getSource.text"</code> <code>"getSource.getText"</code> or | ||
| 623 | * <code>"source.getText"</code>. If a method can not be found, or an | ||
| 624 | * exception is generated as part of invoking a method a | ||
| 625 | * <code>RuntimeException</code> will be thrown at dispatch time. For | ||
| 626 | * example, if the incoming event object is null, and | ||
| 627 | * <code>eventPropertyName</code> is non-null and not empty, a | ||
| 628 | * <code>RuntimeException</code> will be thrown. | ||
| 629 | * <p> | ||
| 630 | * The <code>action</code> argument is of the same format as the | ||
| 631 | * <code>eventPropertyName</code> argument where the last property name | ||
| 632 | * identifies either a method name or writable property. | ||
| 633 | * <p> | ||
| 634 | * If the <code>listenerMethodName</code> is <code>null</code> | ||
| 635 | * <em>all</em> methods in the interface trigger the <code>action</code> to be | ||
| 636 | * executed on the <code>target</code>. | ||
| 637 | * <p> | ||
| 638 | * For example, to create a <code>MouseListener</code> that sets the target | ||
| 639 | * object's <code>origin</code> property to the incoming <code>MouseEvent</code>'s | ||
| 640 | * location (that's the value of <code>mouseEvent.getPoint()</code>) each | ||
| 641 | * time a mouse button is pressed, one would write: | ||
| 642 | *<blockquote> | ||
| 643 | *<pre> | ||
| 644 | *EventHandler.create(MouseListener.class, target, "origin", "point", "mousePressed"); | ||
| 645 | *</pre> | ||
| 646 | *</blockquote> | ||
| 647 | * | ||
| 648 | * This is comparable to writing a <code>MouseListener</code> in which all | ||
| 649 | * of the methods except <code>mousePressed</code> are no-ops: | ||
| 650 | * | ||
| 651 | *<blockquote> | ||
| 652 | *<pre> | ||
| 653 | //Equivalent code using an inner class instead of EventHandler. | ||
| 654 | *new MouseAdapter() { | ||
| 655 | * public void mousePressed(MouseEvent e) { | ||
| 656 | * target.setOrigin(e.getPoint()); | ||
| 657 | * } | ||
| 658 | *}; | ||
| 659 | * </pre> | ||
| 660 | *</blockquote> | ||
| 661 | * | ||
| 662 | * @param listenerInterface the listener interface to create a proxy for | ||
| 663 | * @param target the object that will perform the action | ||
| 664 | * @param action the name of a (possibly qualified) property or method on | ||
| 665 | * the target | ||
| 666 | * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event | ||
| 667 | * @param listenerMethodName the name of the method in the listener interface that should trigger the action | ||
| 668 | * | ||
| 669 | * @return an object that implements <code>listenerInterface</code> | ||
| 670 | * | ||
| 671 | * @throws NullPointerException if <code>listenerInterface</code> is null | ||
| 672 | * @throws NullPointerException if <code>target</code> is null | ||
| 673 | * @throws NullPointerException if <code>action</code> is null | ||
| 674 | * | ||
| 675 | * @see EventHandler | ||
| 676 | */ | ||
| 677 | public static <T> T create(Class<T> listenerInterface, | ||
| 678 | Object target, String action, | ||
| 679 | String eventPropertyName, | ||
| 680 | String listenerMethodName) | ||
| 681 | { | ||
| 682 | // Create this first to verify target/action are non-null | ||
| 683 | EventHandler eventHandler = new EventHandler(target, action, | ||
| 684 | eventPropertyName, | ||
| 685 | listenerMethodName); | ||
| 686 | if (listenerInterface == null) { | ||
| 687 | throw new NullPointerException( | ||
| 688 | "listenerInterface must be non-null"); | ||
| 689 | } | ||
| 690 | return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(), | ||
| 691 | new Class[] {listenerInterface}, | ||
| 692 | eventHandler); | ||
| 693 | } | ||
| 694 | } | ||
Binary file not shown.
| @@ -0,0 +1,536 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.lang.ref.Reference; | ||
| 29 | import java.lang.reflect.Method; | ||
| 30 | import java.lang.reflect.Modifier; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * An EventSetDescriptor describes a group of events that a given Java | ||
| 34 | * bean fires. | ||
| 35 | * <P> | ||
| 36 | * The given group of events are all delivered as method calls on a single | ||
| 37 | * event listener interface, and an event listener object can be registered | ||
| 38 | * via a call on a registration method supplied by the event source. | ||
| 39 | */ | ||
| 40 | public class EventSetDescriptor extends FeatureDescriptor { | ||
| 41 | |||
| 42 | private MethodDescriptor[] listenerMethodDescriptors; | ||
| 43 | private MethodDescriptor addMethodDescriptor; | ||
| 44 | private MethodDescriptor removeMethodDescriptor; | ||
| 45 | private MethodDescriptor getMethodDescriptor; | ||
| 46 | |||
| 47 | private Reference<Method[]> listenerMethodsRef; | ||
| 48 | private Reference<Class> listenerTypeRef; | ||
| 49 | |||
| 50 | private boolean unicast; | ||
| 51 | private boolean inDefaultEventSet = true; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Creates an <TT>EventSetDescriptor</TT> assuming that you are | ||
| 55 | * following the most simple standard design pattern where a named | ||
| 56 | * event "fred" is (1) delivered as a call on the single method of | ||
| 57 | * interface FredListener, (2) has a single argument of type FredEvent, | ||
| 58 | * and (3) where the FredListener may be registered with a call on an | ||
| 59 | * addFredListener method of the source component and removed with a | ||
| 60 | * call on a removeFredListener method. | ||
| 61 | * | ||
| 62 | * @param sourceClass The class firing the event. | ||
| 63 | * @param eventSetName The programmatic name of the event. E.g. "fred". | ||
| 64 | * Note that this should normally start with a lower-case character. | ||
| 65 | * @param listenerType The target interface that events | ||
| 66 | * will get delivered to. | ||
| 67 | * @param listenerMethodName The method that will get called when the event gets | ||
| 68 | * delivered to its target listener interface. | ||
| 69 | * @exception IntrospectionException if an exception occurs during | ||
| 70 | * introspection. | ||
| 71 | */ | ||
| 72 | public EventSetDescriptor(Class<?> sourceClass, String eventSetName, | ||
| 73 | Class<?> listenerType, String listenerMethodName) | ||
| 74 | throws IntrospectionException { | ||
| 75 | this(sourceClass, eventSetName, listenerType, | ||
| 76 | new String[] { listenerMethodName }, | ||
| 77 | Introspector.ADD_PREFIX + getListenerClassName(listenerType), | ||
| 78 | Introspector.REMOVE_PREFIX + getListenerClassName(listenerType), | ||
| 79 | Introspector.GET_PREFIX + getListenerClassName(listenerType) + "s"); | ||
| 80 | |||
| 81 | String eventName = NameGenerator.capitalize(eventSetName) + "Event"; | ||
| 82 | Method[] listenerMethods = getListenerMethods(); | ||
| 83 | if (listenerMethods.length > 0) { | ||
| 84 | Class[] args = getParameterTypes(getClass0(), listenerMethods[0]); | ||
| 85 | // Check for EventSet compliance. Special case for vetoableChange. See 4529996 | ||
| 86 | if (!"vetoableChange".equals(eventSetName) && !args[0].getName().endsWith(eventName)) { | ||
| 87 | throw new IntrospectionException("Method \"" + listenerMethodName + | ||
| 88 | "\" should have argument \"" + | ||
| 89 | eventName + "\""); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | private static String getListenerClassName(Class cls) { | ||
| 95 | String className = cls.getName(); | ||
| 96 | return className.substring(className.lastIndexOf('.') + 1); | ||
| 97 | } | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Creates an <TT>EventSetDescriptor</TT> from scratch using | ||
| 101 | * string names. | ||
| 102 | * | ||
| 103 | * @param sourceClass The class firing the event. | ||
| 104 | * @param eventSetName The programmatic name of the event set. | ||
| 105 | * Note that this should normally start with a lower-case character. | ||
| 106 | * @param listenerType The Class of the target interface that events | ||
| 107 | * will get delivered to. | ||
| 108 | * @param listenerMethodNames The names of the methods that will get called | ||
| 109 | * when the event gets delivered to its target listener interface. | ||
| 110 | * @param addListenerMethodName The name of the method on the event source | ||
| 111 | * that can be used to register an event listener object. | ||
| 112 | * @param removeListenerMethodName The name of the method on the event source | ||
| 113 | * that can be used to de-register an event listener object. | ||
| 114 | * @exception IntrospectionException if an exception occurs during | ||
| 115 | * introspection. | ||
| 116 | */ | ||
| 117 | public EventSetDescriptor(Class<?> sourceClass, | ||
| 118 | String eventSetName, | ||
| 119 | Class<?> listenerType, | ||
| 120 | String listenerMethodNames[], | ||
| 121 | String addListenerMethodName, | ||
| 122 | String removeListenerMethodName) | ||
| 123 | throws IntrospectionException { | ||
| 124 | this(sourceClass, eventSetName, listenerType, | ||
| 125 | listenerMethodNames, addListenerMethodName, | ||
| 126 | removeListenerMethodName, null); | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * This constructor creates an EventSetDescriptor from scratch using | ||
| 131 | * string names. | ||
| 132 | * | ||
| 133 | * @param sourceClass The class firing the event. | ||
| 134 | * @param eventSetName The programmatic name of the event set. | ||
| 135 | * Note that this should normally start with a lower-case character. | ||
| 136 | * @param listenerType The Class of the target interface that events | ||
| 137 | * will get delivered to. | ||
| 138 | * @param listenerMethodNames The names of the methods that will get called | ||
| 139 | * when the event gets delivered to its target listener interface. | ||
| 140 | * @param addListenerMethodName The name of the method on the event source | ||
| 141 | * that can be used to register an event listener object. | ||
| 142 | * @param removeListenerMethodName The name of the method on the event source | ||
| 143 | * that can be used to de-register an event listener object. | ||
| 144 | * @param getListenerMethodName The method on the event source that | ||
| 145 | * can be used to access the array of event listener objects. | ||
| 146 | * @exception IntrospectionException if an exception occurs during | ||
| 147 | * introspection. | ||
| 148 | * @since 1.4 | ||
| 149 | */ | ||
| 150 | public EventSetDescriptor(Class<?> sourceClass, | ||
| 151 | String eventSetName, | ||
| 152 | Class<?> listenerType, | ||
| 153 | String listenerMethodNames[], | ||
| 154 | String addListenerMethodName, | ||
| 155 | String removeListenerMethodName, | ||
| 156 | String getListenerMethodName) | ||
| 157 | throws IntrospectionException { | ||
| 158 | if (sourceClass == null || eventSetName == null || listenerType == null) { | ||
| 159 | throw new NullPointerException(); | ||
| 160 | } | ||
| 161 | setName(eventSetName); | ||
| 162 | setClass0(sourceClass); | ||
| 163 | setListenerType(listenerType); | ||
| 164 | |||
| 165 | Method[] listenerMethods = new Method[listenerMethodNames.length]; | ||
| 166 | for (int i = 0; i < listenerMethodNames.length; i++) { | ||
| 167 | // Check for null names | ||
| 168 | if (listenerMethodNames[i] == null) { | ||
| 169 | throw new NullPointerException(); | ||
| 170 | } | ||
| 171 | listenerMethods[i] = getMethod(listenerType, listenerMethodNames[i], 1); | ||
| 172 | } | ||
| 173 | setListenerMethods(listenerMethods); | ||
| 174 | |||
| 175 | setAddListenerMethod(getMethod(sourceClass, addListenerMethodName, 1)); | ||
| 176 | setRemoveListenerMethod(getMethod(sourceClass, removeListenerMethodName, 1)); | ||
| 177 | |||
| 178 | // Be more forgiving of not finding the getListener method. | ||
| 179 | Method method = Introspector.findMethod(sourceClass, getListenerMethodName, 0); | ||
| 180 | if (method != null) { | ||
| 181 | setGetListenerMethod(method); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | private static Method getMethod(Class cls, String name, int args) | ||
| 186 | throws IntrospectionException { | ||
| 187 | if (name == null) { | ||
| 188 | return null; | ||
| 189 | } | ||
| 190 | Method method = Introspector.findMethod(cls, name, args); | ||
| 191 | if ((method == null) || Modifier.isStatic(method.getModifiers())) { | ||
| 192 | throw new IntrospectionException("Method not found: " + name + | ||
| 193 | " on class " + cls.getName()); | ||
| 194 | } | ||
| 195 | return method; | ||
| 196 | } | ||
| 197 | |||
| 198 | /** | ||
| 199 | * Creates an <TT>EventSetDescriptor</TT> from scratch using | ||
| 200 | * <TT>java.lang.reflect.Method</TT> and <TT>java.lang.Class</TT> objects. | ||
| 201 | * | ||
| 202 | * @param eventSetName The programmatic name of the event set. | ||
| 203 | * @param listenerType The Class for the listener interface. | ||
| 204 | * @param listenerMethods An array of Method objects describing each | ||
| 205 | * of the event handling methods in the target listener. | ||
| 206 | * @param addListenerMethod The method on the event source | ||
| 207 | * that can be used to register an event listener object. | ||
| 208 | * @param removeListenerMethod The method on the event source | ||
| 209 | * that can be used to de-register an event listener object. | ||
| 210 | * @exception IntrospectionException if an exception occurs during | ||
| 211 | * introspection. | ||
| 212 | */ | ||
| 213 | public EventSetDescriptor(String eventSetName, | ||
| 214 | Class<?> listenerType, | ||
| 215 | Method listenerMethods[], | ||
| 216 | Method addListenerMethod, | ||
| 217 | Method removeListenerMethod) | ||
| 218 | throws IntrospectionException { | ||
| 219 | this(eventSetName, listenerType, listenerMethods, | ||
| 220 | addListenerMethod, removeListenerMethod, null); | ||
| 221 | } | ||
| 222 | |||
| 223 | /** | ||
| 224 | * This constructor creates an EventSetDescriptor from scratch using | ||
| 225 | * java.lang.reflect.Method and java.lang.Class objects. | ||
| 226 | * | ||
| 227 | * @param eventSetName The programmatic name of the event set. | ||
| 228 | * @param listenerType The Class for the listener interface. | ||
| 229 | * @param listenerMethods An array of Method objects describing each | ||
| 230 | * of the event handling methods in the target listener. | ||
| 231 | * @param addListenerMethod The method on the event source | ||
| 232 | * that can be used to register an event listener object. | ||
| 233 | * @param removeListenerMethod The method on the event source | ||
| 234 | * that can be used to de-register an event listener object. | ||
| 235 | * @param getListenerMethod The method on the event source | ||
| 236 | * that can be used to access the array of event listener objects. | ||
| 237 | * @exception IntrospectionException if an exception occurs during | ||
| 238 | * introspection. | ||
| 239 | * @since 1.4 | ||
| 240 | */ | ||
| 241 | public EventSetDescriptor(String eventSetName, | ||
| 242 | Class<?> listenerType, | ||
| 243 | Method listenerMethods[], | ||
| 244 | Method addListenerMethod, | ||
| 245 | Method removeListenerMethod, | ||
| 246 | Method getListenerMethod) | ||
| 247 | throws IntrospectionException { | ||
| 248 | setName(eventSetName); | ||
| 249 | setListenerMethods(listenerMethods); | ||
| 250 | setAddListenerMethod(addListenerMethod); | ||
| 251 | setRemoveListenerMethod( removeListenerMethod); | ||
| 252 | setGetListenerMethod(getListenerMethod); | ||
| 253 | setListenerType(listenerType); | ||
| 254 | } | ||
| 255 | |||
| 256 | /** | ||
| 257 | * Creates an <TT>EventSetDescriptor</TT> from scratch using | ||
| 258 | * <TT>java.lang.reflect.MethodDescriptor</TT> and <TT>java.lang.Class</TT> | ||
| 259 | * objects. | ||
| 260 | * | ||
| 261 | * @param eventSetName The programmatic name of the event set. | ||
| 262 | * @param listenerType The Class for the listener interface. | ||
| 263 | * @param listenerMethodDescriptors An array of MethodDescriptor objects | ||
| 264 | * describing each of the event handling methods in the | ||
| 265 | * target listener. | ||
| 266 | * @param addListenerMethod The method on the event source | ||
| 267 | * that can be used to register an event listener object. | ||
| 268 | * @param removeListenerMethod The method on the event source | ||
| 269 | * that can be used to de-register an event listener object. | ||
| 270 | * @exception IntrospectionException if an exception occurs during | ||
| 271 | * introspection. | ||
| 272 | */ | ||
| 273 | public EventSetDescriptor(String eventSetName, | ||
| 274 | Class<?> listenerType, | ||
| 275 | MethodDescriptor listenerMethodDescriptors[], | ||
| 276 | Method addListenerMethod, | ||
| 277 | Method removeListenerMethod) | ||
| 278 | throws IntrospectionException { | ||
| 279 | setName(eventSetName); | ||
| 280 | this.listenerMethodDescriptors = listenerMethodDescriptors; | ||
| 281 | setAddListenerMethod(addListenerMethod); | ||
| 282 | setRemoveListenerMethod(removeListenerMethod); | ||
| 283 | setListenerType(listenerType); | ||
| 284 | } | ||
| 285 | |||
| 286 | /** | ||
| 287 | * Gets the <TT>Class</TT> object for the target interface. | ||
| 288 | * | ||
| 289 | * @return The Class object for the target interface that will | ||
| 290 | * get invoked when the event is fired. | ||
| 291 | */ | ||
| 292 | public Class<?> getListenerType() { | ||
| 293 | return (this.listenerTypeRef != null) | ||
| 294 | ? this.listenerTypeRef.get() | ||
| 295 | : null; | ||
| 296 | } | ||
| 297 | |||
| 298 | private void setListenerType(Class cls) { | ||
| 299 | this.listenerTypeRef = getWeakReference(cls); | ||
| 300 | } | ||
| 301 | |||
| 302 | /** | ||
| 303 | * Gets the methods of the target listener interface. | ||
| 304 | * | ||
| 305 | * @return An array of <TT>Method</TT> objects for the target methods | ||
| 306 | * within the target listener interface that will get called when | ||
| 307 | * events are fired. | ||
| 308 | */ | ||
| 309 | public synchronized Method[] getListenerMethods() { | ||
| 310 | Method[] methods = getListenerMethods0(); | ||
| 311 | if (methods == null) { | ||
| 312 | if (listenerMethodDescriptors != null) { | ||
| 313 | methods = new Method[listenerMethodDescriptors.length]; | ||
| 314 | for (int i = 0; i < methods.length; i++) { | ||
| 315 | methods[i] = listenerMethodDescriptors[i].getMethod(); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | setListenerMethods(methods); | ||
| 319 | } | ||
| 320 | return methods; | ||
| 321 | } | ||
| 322 | |||
| 323 | private void setListenerMethods(Method[] methods) { | ||
| 324 | if (methods == null) { | ||
| 325 | return; | ||
| 326 | } | ||
| 327 | if (listenerMethodDescriptors == null) { | ||
| 328 | listenerMethodDescriptors = new MethodDescriptor[methods.length]; | ||
| 329 | for (int i = 0; i < methods.length; i++) { | ||
| 330 | listenerMethodDescriptors[i] = new MethodDescriptor(methods[i]); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | this.listenerMethodsRef = getSoftReference(methods); | ||
| 334 | } | ||
| 335 | |||
| 336 | private Method[] getListenerMethods0() { | ||
| 337 | return (this.listenerMethodsRef != null) | ||
| 338 | ? this.listenerMethodsRef.get() | ||
| 339 | : null; | ||
| 340 | } | ||
| 341 | |||
| 342 | /** | ||
| 343 | * Gets the <code>MethodDescriptor</code>s of the target listener interface. | ||
| 344 | * | ||
| 345 | * @return An array of <code>MethodDescriptor</code> objects for the target methods | ||
| 346 | * within the target listener interface that will get called when | ||
| 347 | * events are fired. | ||
| 348 | */ | ||
| 349 | public synchronized MethodDescriptor[] getListenerMethodDescriptors() { | ||
| 350 | return listenerMethodDescriptors; | ||
| 351 | } | ||
| 352 | |||
| 353 | /** | ||
| 354 | * Gets the method used to add event listeners. | ||
| 355 | * | ||
| 356 | * @return The method used to register a listener at the event source. | ||
| 357 | */ | ||
| 358 | public synchronized Method getAddListenerMethod() { | ||
| 359 | return getMethod(this.addMethodDescriptor); | ||
| 360 | } | ||
| 361 | |||
| 362 | private synchronized void setAddListenerMethod(Method method) { | ||
| 363 | if (method == null) { | ||
| 364 | return; | ||
| 365 | } | ||
| 366 | if (getClass0() == null) { | ||
| 367 | setClass0(method.getDeclaringClass()); | ||
| 368 | } | ||
| 369 | addMethodDescriptor = new MethodDescriptor(method); | ||
| 370 | setTransient(method.getAnnotation(Transient.class)); | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Gets the method used to remove event listeners. | ||
| 375 | * | ||
| 376 | * @return The method used to remove a listener at the event source. | ||
| 377 | */ | ||
| 378 | public synchronized Method getRemoveListenerMethod() { | ||
| 379 | return getMethod(this.removeMethodDescriptor); | ||
| 380 | } | ||
| 381 | |||
| 382 | private synchronized void setRemoveListenerMethod(Method method) { | ||
| 383 | if (method == null) { | ||
| 384 | return; | ||
| 385 | } | ||
| 386 | if (getClass0() == null) { | ||
| 387 | setClass0(method.getDeclaringClass()); | ||
| 388 | } | ||
| 389 | removeMethodDescriptor = new MethodDescriptor(method); | ||
| 390 | setTransient(method.getAnnotation(Transient.class)); | ||
| 391 | } | ||
| 392 | |||
| 393 | /** | ||
| 394 | * Gets the method used to access the registered event listeners. | ||
| 395 | * | ||
| 396 | * @return The method used to access the array of listeners at the event | ||
| 397 | * source or null if it doesn't exist. | ||
| 398 | * @since 1.4 | ||
| 399 | */ | ||
| 400 | public synchronized Method getGetListenerMethod() { | ||
| 401 | return getMethod(this.getMethodDescriptor); | ||
| 402 | } | ||
| 403 | |||
| 404 | private synchronized void setGetListenerMethod(Method method) { | ||
| 405 | if (method == null) { | ||
| 406 | return; | ||
| 407 | } | ||
| 408 | if (getClass0() == null) { | ||
| 409 | setClass0(method.getDeclaringClass()); | ||
| 410 | } | ||
| 411 | getMethodDescriptor = new MethodDescriptor(method); | ||
| 412 | setTransient(method.getAnnotation(Transient.class)); | ||
| 413 | } | ||
| 414 | |||
| 415 | /** | ||
| 416 | * Mark an event set as unicast (or not). | ||
| 417 | * | ||
| 418 | * @param unicast True if the event set is unicast. | ||
| 419 | */ | ||
| 420 | public void setUnicast(boolean unicast) { | ||
| 421 | this.unicast = unicast; | ||
| 422 | } | ||
| 423 | |||
| 424 | /** | ||
| 425 | * Normally event sources are multicast. However there are some | ||
| 426 | * exceptions that are strictly unicast. | ||
| 427 | * | ||
| 428 | * @return <TT>true</TT> if the event set is unicast. | ||
| 429 | * Defaults to <TT>false</TT>. | ||
| 430 | */ | ||
| 431 | public boolean isUnicast() { | ||
| 432 | return unicast; | ||
| 433 | } | ||
| 434 | |||
| 435 | /** | ||
| 436 | * Marks an event set as being in the "default" set (or not). | ||
| 437 | * By default this is <TT>true</TT>. | ||
| 438 | * | ||
| 439 | * @param inDefaultEventSet <code>true</code> if the event set is in | ||
| 440 | * the "default" set, | ||
| 441 | * <code>false</code> if not | ||
| 442 | */ | ||
| 443 | public void setInDefaultEventSet(boolean inDefaultEventSet) { | ||
| 444 | this.inDefaultEventSet = inDefaultEventSet; | ||
| 445 | } | ||
| 446 | |||
| 447 | /** | ||
| 448 | * Reports if an event set is in the "default" set. | ||
| 449 | * | ||
| 450 | * @return <TT>true</TT> if the event set is in | ||
| 451 | * the "default" set. Defaults to <TT>true</TT>. | ||
| 452 | */ | ||
| 453 | public boolean isInDefaultEventSet() { | ||
| 454 | return inDefaultEventSet; | ||
| 455 | } | ||
| 456 | |||
| 457 | /* | ||
| 458 | * Package-private constructor | ||
| 459 | * Merge two event set descriptors. Where they conflict, give the | ||
| 460 | * second argument (y) priority over the first argument (x). | ||
| 461 | * | ||
| 462 | * @param x The first (lower priority) EventSetDescriptor | ||
| 463 | * @param y The second (higher priority) EventSetDescriptor | ||
| 464 | */ | ||
| 465 | EventSetDescriptor(EventSetDescriptor x, EventSetDescriptor y) { | ||
| 466 | super(x,y); | ||
| 467 | listenerMethodDescriptors = x.listenerMethodDescriptors; | ||
| 468 | if (y.listenerMethodDescriptors != null) { | ||
| 469 | listenerMethodDescriptors = y.listenerMethodDescriptors; | ||
| 470 | } | ||
| 471 | |||
| 472 | listenerTypeRef = x.listenerTypeRef; | ||
| 473 | if (y.listenerTypeRef != null) { | ||
| 474 | listenerTypeRef = y.listenerTypeRef; | ||
| 475 | } | ||
| 476 | |||
| 477 | addMethodDescriptor = x.addMethodDescriptor; | ||
| 478 | if (y.addMethodDescriptor != null) { | ||
| 479 | addMethodDescriptor = y.addMethodDescriptor; | ||
| 480 | } | ||
| 481 | |||
| 482 | removeMethodDescriptor = x.removeMethodDescriptor; | ||
| 483 | if (y.removeMethodDescriptor != null) { | ||
| 484 | removeMethodDescriptor = y.removeMethodDescriptor; | ||
| 485 | } | ||
| 486 | |||
| 487 | getMethodDescriptor = x.getMethodDescriptor; | ||
| 488 | if (y.getMethodDescriptor != null) { | ||
| 489 | getMethodDescriptor = y.getMethodDescriptor; | ||
| 490 | } | ||
| 491 | |||
| 492 | unicast = y.unicast; | ||
| 493 | if (!x.inDefaultEventSet || !y.inDefaultEventSet) { | ||
| 494 | inDefaultEventSet = false; | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | /* | ||
| 499 | * Package-private dup constructor | ||
| 500 | * This must isolate the new object from any changes to the old object. | ||
| 501 | */ | ||
| 502 | EventSetDescriptor(EventSetDescriptor old) { | ||
| 503 | super(old); | ||
| 504 | if (old.listenerMethodDescriptors != null) { | ||
| 505 | int len = old.listenerMethodDescriptors.length; | ||
| 506 | listenerMethodDescriptors = new MethodDescriptor[len]; | ||
| 507 | for (int i = 0; i < len; i++) { | ||
| 508 | listenerMethodDescriptors[i] = new MethodDescriptor( | ||
| 509 | old.listenerMethodDescriptors[i]); | ||
| 510 | } | ||
| 511 | } | ||
| 512 | listenerTypeRef = old.listenerTypeRef; | ||
| 513 | |||
| 514 | addMethodDescriptor = old.addMethodDescriptor; | ||
| 515 | removeMethodDescriptor = old.removeMethodDescriptor; | ||
| 516 | getMethodDescriptor = old.getMethodDescriptor; | ||
| 517 | |||
| 518 | unicast = old.unicast; | ||
| 519 | inDefaultEventSet = old.inDefaultEventSet; | ||
| 520 | } | ||
| 521 | |||
| 522 | void appendTo(StringBuilder sb) { | ||
| 523 | appendTo(sb, "unicast", this.unicast); | ||
| 524 | appendTo(sb, "inDefaultEventSet", this.inDefaultEventSet); | ||
| 525 | appendTo(sb, "listenerType", this.listenerTypeRef); | ||
| 526 | appendTo(sb, "getListenerMethod", getMethod(this.getMethodDescriptor)); | ||
| 527 | appendTo(sb, "addListenerMethod", getMethod(this.addMethodDescriptor)); | ||
| 528 | appendTo(sb, "removeListenerMethod", getMethod(this.removeMethodDescriptor)); | ||
| 529 | } | ||
| 530 | |||
| 531 | private static Method getMethod(MethodDescriptor descriptor) { | ||
| 532 | return (descriptor != null) | ||
| 533 | ? descriptor.getMethod() | ||
| 534 | : null; | ||
| 535 | } | ||
| 536 | } | ||
Binary file not shown.
| @@ -0,0 +1,43 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * An ExceptionListener is notified of internal exceptions. | ||
| 29 | * | ||
| 30 | * @since 1.4 | ||
| 31 | * | ||
| 32 | * @author Philip Milne | ||
| 33 | */ | ||
| 34 | public interface ExceptionListener { | ||
| 35 | /** | ||
| 36 | * This method is called when a recoverable exception has | ||
| 37 | * been caught. | ||
| 38 | * | ||
| 39 | * @param e The exception that was caught. | ||
| 40 | * | ||
| 41 | */ | ||
| 42 | public void exceptionThrown(Exception e); | ||
| 43 | } | ||
Binary file not shown.
| @@ -0,0 +1,182 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * An <code>Expression</code> object represents a primitive expression | ||
| 30 | * in which a single method is applied to a target and a set of | ||
| 31 | * arguments to return a result - as in <code>"a.getFoo()"</code>. | ||
| 32 | * <p> | ||
| 33 | * In addition to the properties of the super class, the | ||
| 34 | * <code>Expression</code> object provides a <em>value</em> which | ||
| 35 | * is the object returned when this expression is evaluated. | ||
| 36 | * The return value is typically not provided by the caller and | ||
| 37 | * is instead computed by dynamically finding the method and invoking | ||
| 38 | * it when the first call to <code>getValue</code> is made. | ||
| 39 | * | ||
| 40 | * @see #getValue | ||
| 41 | * @see #setValue | ||
| 42 | * | ||
| 43 | * @since 1.4 | ||
| 44 | * | ||
| 45 | * @author Philip Milne | ||
| 46 | */ | ||
| 47 | public class Expression extends Statement { | ||
| 48 | |||
| 49 | private static Object unbound = new Object(); | ||
| 50 | |||
| 51 | private Object value = unbound; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Creates a new {@link Expression} object | ||
| 55 | * for the specified target object to invoke the method | ||
| 56 | * specified by the name and by the array of arguments. | ||
| 57 | * <p> | ||
| 58 | * The {@code target} and the {@code methodName} values should not be {@code null}. | ||
| 59 | * Otherwise an attempt to execute this {@code Expression} | ||
| 60 | * will result in a {@code NullPointerException}. | ||
| 61 | * If the {@code arguments} value is {@code null}, | ||
| 62 | * an empty array is used as the value of the {@code arguments} property. | ||
| 63 | * | ||
| 64 | * @param target the target object of this expression | ||
| 65 | * @param methodName the name of the method to invoke on the specified target | ||
| 66 | * @param arguments the array of arguments to invoke the specified method | ||
| 67 | * | ||
| 68 | * @see #getValue | ||
| 69 | */ | ||
| 70 | @ConstructorProperties({"target", "methodName", "arguments"}) | ||
| 71 | public Expression(Object target, String methodName, Object[] arguments) { | ||
| 72 | super(target, methodName, arguments); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Creates a new {@link Expression} object with the specified value | ||
| 77 | * for the specified target object to invoke the method | ||
| 78 | * specified by the name and by the array of arguments. | ||
| 79 | * The {@code value} value is used as the value of the {@code value} property, | ||
| 80 | * so the {@link #getValue} method will return it | ||
| 81 | * without executing this {@code Expression}. | ||
| 82 | * <p> | ||
| 83 | * The {@code target} and the {@code methodName} values should not be {@code null}. | ||
| 84 | * Otherwise an attempt to execute this {@code Expression} | ||
| 85 | * will result in a {@code NullPointerException}. | ||
| 86 | * If the {@code arguments} value is {@code null}, | ||
| 87 | * an empty array is used as the value of the {@code arguments} property. | ||
| 88 | * | ||
| 89 | * @param value the value of this expression | ||
| 90 | * @param target the target object of this expression | ||
| 91 | * @param methodName the name of the method to invoke on the specified target | ||
| 92 | * @param arguments the array of arguments to invoke the specified method | ||
| 93 | * | ||
| 94 | * @see #setValue | ||
| 95 | */ | ||
| 96 | public Expression(Object value, Object target, String methodName, Object[] arguments) { | ||
| 97 | this(target, methodName, arguments); | ||
| 98 | setValue(value); | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * {@inheritDoc} | ||
| 103 | * <p> | ||
| 104 | * If the invoked method completes normally, | ||
| 105 | * the value it returns is copied in the {@code value} property. | ||
| 106 | * Note that the {@code value} property is set to {@code null}, | ||
| 107 | * if the return type of the underlying method is {@code void}. | ||
| 108 | * | ||
| 109 | * @throws NullPointerException if the value of the {@code target} or | ||
| 110 | * {@code methodName} property is {@code null} | ||
| 111 | * @throws NoSuchMethodException if a matching method is not found | ||
| 112 | * @throws SecurityException if a security manager exists and | ||
| 113 | * it denies the method invocation | ||
| 114 | * @throws Exception that is thrown by the invoked method | ||
| 115 | * | ||
| 116 | * @see java.lang.reflect.Method | ||
| 117 | * @since 1.7 | ||
| 118 | */ | ||
| 119 | @Override | ||
| 120 | public void execute() throws Exception { | ||
| 121 | setValue(invoke()); | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * If the value property of this instance is not already set, | ||
| 126 | * this method dynamically finds the method with the specified | ||
| 127 | * methodName on this target with these arguments and calls it. | ||
| 128 | * The result of the method invocation is first copied | ||
| 129 | * into the value property of this expression and then returned | ||
| 130 | * as the result of <code>getValue</code>. If the value property | ||
| 131 | * was already set, either by a call to <code>setValue</code> | ||
| 132 | * or a previous call to <code>getValue</code> then the value | ||
| 133 | * property is returned without either looking up or calling the method. | ||
| 134 | * <p> | ||
| 135 | * The value property of an <code>Expression</code> is set to | ||
| 136 | * a unique private (non-<code>null</code>) value by default and | ||
| 137 | * this value is used as an internal indication that the method | ||
| 138 | * has not yet been called. A return value of <code>null</code> | ||
| 139 | * replaces this default value in the same way that any other value | ||
| 140 | * would, ensuring that expressions are never evaluated more than once. | ||
| 141 | * <p> | ||
| 142 | * See the <code>excecute</code> method for details on how | ||
| 143 | * methods are chosen using the dynamic types of the target | ||
| 144 | * and arguments. | ||
| 145 | * | ||
| 146 | * @see Statement#execute | ||
| 147 | * @see #setValue | ||
| 148 | * | ||
| 149 | * @return The result of applying this method to these arguments. | ||
| 150 | */ | ||
| 151 | public Object getValue() throws Exception { | ||
| 152 | if (value == unbound) { | ||
| 153 | setValue(invoke()); | ||
| 154 | } | ||
| 155 | return value; | ||
| 156 | } | ||
| 157 | |||
| 158 | /** | ||
| 159 | * Sets the value of this expression to <code>value</code>. | ||
| 160 | * This value will be returned by the getValue method | ||
| 161 | * without calling the method associated with this | ||
| 162 | * expression. | ||
| 163 | * | ||
| 164 | * @param value The value of this expression. | ||
| 165 | * | ||
| 166 | * @see #getValue | ||
| 167 | */ | ||
| 168 | public void setValue(Object value) { | ||
| 169 | this.value = value; | ||
| 170 | } | ||
| 171 | |||
| 172 | /*pp*/ String instanceName(Object instance) { | ||
| 173 | return instance == unbound ? "<unbound>" : super.instanceName(instance); | ||
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * Prints the value of this expression using a Java-style syntax. | ||
| 178 | */ | ||
| 179 | public String toString() { | ||
| 180 | return instanceName(value) + "=" + super.toString(); | ||
| 181 | } | ||
| 182 | } | ||
Binary file not shown.
| @@ -0,0 +1,445 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import com.sun.beans.TypeResolver; | ||
| 29 | |||
| 30 | import java.lang.ref.Reference; | ||
| 31 | import java.lang.ref.WeakReference; | ||
| 32 | import java.lang.ref.SoftReference; | ||
| 33 | |||
| 34 | import java.lang.reflect.Method; | ||
| 35 | |||
| 36 | import java.util.Enumeration; | ||
| 37 | import java.util.Hashtable; | ||
| 38 | import java.util.Map.Entry; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * The FeatureDescriptor class is the common baseclass for PropertyDescriptor, | ||
| 42 | * EventSetDescriptor, and MethodDescriptor, etc. | ||
| 43 | * <p> | ||
| 44 | * It supports some common information that can be set and retrieved for | ||
| 45 | * any of the introspection descriptors. | ||
| 46 | * <p> | ||
| 47 | * In addition it provides an extension mechanism so that arbitrary | ||
| 48 | * attribute/value pairs can be associated with a design feature. | ||
| 49 | */ | ||
| 50 | |||
| 51 | public class FeatureDescriptor { | ||
| 52 | private static final String TRANSIENT = "transient"; | ||
| 53 | |||
| 54 | private Reference<Class> classRef; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Constructs a <code>FeatureDescriptor</code>. | ||
| 58 | */ | ||
| 59 | public FeatureDescriptor() { | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Gets the programmatic name of this feature. | ||
| 64 | * | ||
| 65 | * @return The programmatic name of the property/method/event | ||
| 66 | */ | ||
| 67 | public String getName() { | ||
| 68 | return name; | ||
| 69 | } | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Sets the programmatic name of this feature. | ||
| 73 | * | ||
| 74 | * @param name The programmatic name of the property/method/event | ||
| 75 | */ | ||
| 76 | public void setName(String name) { | ||
| 77 | this.name = name; | ||
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Gets the localized display name of this feature. | ||
| 82 | * | ||
| 83 | * @return The localized display name for the property/method/event. | ||
| 84 | * This defaults to the same as its programmatic name from getName. | ||
| 85 | */ | ||
| 86 | public String getDisplayName() { | ||
| 87 | if (displayName == null) { | ||
| 88 | return getName(); | ||
| 89 | } | ||
| 90 | return displayName; | ||
| 91 | } | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Sets the localized display name of this feature. | ||
| 95 | * | ||
| 96 | * @param displayName The localized display name for the | ||
| 97 | * property/method/event. | ||
| 98 | */ | ||
| 99 | public void setDisplayName(String displayName) { | ||
| 100 | this.displayName = displayName; | ||
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * The "expert" flag is used to distinguish between those features that are | ||
| 105 | * intended for expert users from those that are intended for normal users. | ||
| 106 | * | ||
| 107 | * @return True if this feature is intended for use by experts only. | ||
| 108 | */ | ||
| 109 | public boolean isExpert() { | ||
| 110 | return expert; | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * The "expert" flag is used to distinguish between features that are | ||
| 115 | * intended for expert users from those that are intended for normal users. | ||
| 116 | * | ||
| 117 | * @param expert True if this feature is intended for use by experts only. | ||
| 118 | */ | ||
| 119 | public void setExpert(boolean expert) { | ||
| 120 | this.expert = expert; | ||
| 121 | } | ||
| 122 | |||
| 123 | /** | ||
| 124 | * The "hidden" flag is used to identify features that are intended only | ||
| 125 | * for tool use, and which should not be exposed to humans. | ||
| 126 | * | ||
| 127 | * @return True if this feature should be hidden from human users. | ||
| 128 | */ | ||
| 129 | public boolean isHidden() { | ||
| 130 | return hidden; | ||
| 131 | } | ||
| 132 | |||
| 133 | /** | ||
| 134 | * The "hidden" flag is used to identify features that are intended only | ||
| 135 | * for tool use, and which should not be exposed to humans. | ||
| 136 | * | ||
| 137 | * @param hidden True if this feature should be hidden from human users. | ||
| 138 | */ | ||
| 139 | public void setHidden(boolean hidden) { | ||
| 140 | this.hidden = hidden; | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * The "preferred" flag is used to identify features that are particularly | ||
| 145 | * important for presenting to humans. | ||
| 146 | * | ||
| 147 | * @return True if this feature should be preferentially shown to human users. | ||
| 148 | */ | ||
| 149 | public boolean isPreferred() { | ||
| 150 | return preferred; | ||
| 151 | } | ||
| 152 | |||
| 153 | /** | ||
| 154 | * The "preferred" flag is used to identify features that are particularly | ||
| 155 | * important for presenting to humans. | ||
| 156 | * | ||
| 157 | * @param preferred True if this feature should be preferentially shown | ||
| 158 | * to human users. | ||
| 159 | */ | ||
| 160 | public void setPreferred(boolean preferred) { | ||
| 161 | this.preferred = preferred; | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * Gets the short description of this feature. | ||
| 166 | * | ||
| 167 | * @return A localized short description associated with this | ||
| 168 | * property/method/event. This defaults to be the display name. | ||
| 169 | */ | ||
| 170 | public String getShortDescription() { | ||
| 171 | if (shortDescription == null) { | ||
| 172 | return getDisplayName(); | ||
| 173 | } | ||
| 174 | return shortDescription; | ||
| 175 | } | ||
| 176 | |||
| 177 | /** | ||
| 178 | * You can associate a short descriptive string with a feature. Normally | ||
| 179 | * these descriptive strings should be less than about 40 characters. | ||
| 180 | * @param text A (localized) short description to be associated with | ||
| 181 | * this property/method/event. | ||
| 182 | */ | ||
| 183 | public void setShortDescription(String text) { | ||
| 184 | shortDescription = text; | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * Associate a named attribute with this feature. | ||
| 189 | * | ||
| 190 | * @param attributeName The locale-independent name of the attribute | ||
| 191 | * @param value The value. | ||
| 192 | */ | ||
| 193 | public void setValue(String attributeName, Object value) { | ||
| 194 | getTable().put(attributeName, value); | ||
| 195 | } | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Retrieve a named attribute with this feature. | ||
| 199 | * | ||
| 200 | * @param attributeName The locale-independent name of the attribute | ||
| 201 | * @return The value of the attribute. May be null if | ||
| 202 | * the attribute is unknown. | ||
| 203 | */ | ||
| 204 | public Object getValue(String attributeName) { | ||
| 205 | return (this.table != null) | ||
| 206 | ? this.table.get(attributeName) | ||
| 207 | : null; | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Gets an enumeration of the locale-independent names of this | ||
| 212 | * feature. | ||
| 213 | * | ||
| 214 | * @return An enumeration of the locale-independent names of any | ||
| 215 | * attributes that have been registered with setValue. | ||
| 216 | */ | ||
| 217 | public Enumeration<String> attributeNames() { | ||
| 218 | return getTable().keys(); | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * Package-private constructor, | ||
| 223 | * Merge information from two FeatureDescriptors. | ||
| 224 | * The merged hidden and expert flags are formed by or-ing the values. | ||
| 225 | * In the event of other conflicts, the second argument (y) is | ||
| 226 | * given priority over the first argument (x). | ||
| 227 | * | ||
| 228 | * @param x The first (lower priority) MethodDescriptor | ||
| 229 | * @param y The second (higher priority) MethodDescriptor | ||
| 230 | */ | ||
| 231 | FeatureDescriptor(FeatureDescriptor x, FeatureDescriptor y) { | ||
| 232 | expert = x.expert | y.expert; | ||
| 233 | hidden = x.hidden | y.hidden; | ||
| 234 | preferred = x.preferred | y.preferred; | ||
| 235 | name = y.name; | ||
| 236 | shortDescription = x.shortDescription; | ||
| 237 | if (y.shortDescription != null) { | ||
| 238 | shortDescription = y.shortDescription; | ||
| 239 | } | ||
| 240 | displayName = x.displayName; | ||
| 241 | if (y.displayName != null) { | ||
| 242 | displayName = y.displayName; | ||
| 243 | } | ||
| 244 | classRef = x.classRef; | ||
| 245 | if (y.classRef != null) { | ||
| 246 | classRef = y.classRef; | ||
| 247 | } | ||
| 248 | addTable(x.table); | ||
| 249 | addTable(y.table); | ||
| 250 | } | ||
| 251 | |||
| 252 | /* | ||
| 253 | * Package-private dup constructor | ||
| 254 | * This must isolate the new object from any changes to the old object. | ||
| 255 | */ | ||
| 256 | FeatureDescriptor(FeatureDescriptor old) { | ||
| 257 | expert = old.expert; | ||
| 258 | hidden = old.hidden; | ||
| 259 | preferred = old.preferred; | ||
| 260 | name = old.name; | ||
| 261 | shortDescription = old.shortDescription; | ||
| 262 | displayName = old.displayName; | ||
| 263 | classRef = old.classRef; | ||
| 264 | |||
| 265 | addTable(old.table); | ||
| 266 | } | ||
| 267 | |||
| 268 | /** | ||
| 269 | * Copies all values from the specified attribute table. | ||
| 270 | * If some attribute is exist its value should be overridden. | ||
| 271 | * | ||
| 272 | * @param table the attribute table with new values | ||
| 273 | */ | ||
| 274 | private void addTable(Hashtable<String, Object> table) { | ||
| 275 | if ((table != null) && !table.isEmpty()) { | ||
| 276 | getTable().putAll(table); | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | /** | ||
| 281 | * Returns the initialized attribute table. | ||
| 282 | * | ||
| 283 | * @return the initialized attribute table | ||
| 284 | */ | ||
| 285 | private Hashtable<String, Object> getTable() { | ||
| 286 | if (this.table == null) { | ||
| 287 | this.table = new Hashtable<String, Object>(); | ||
| 288 | } | ||
| 289 | return this.table; | ||
| 290 | } | ||
| 291 | |||
| 292 | /** | ||
| 293 | * Sets the "transient" attribute according to the annotation. | ||
| 294 | * If the "transient" attribute is already set | ||
| 295 | * it should not be changed. | ||
| 296 | * | ||
| 297 | * @param annotation the annotation of the element of the feature | ||
| 298 | */ | ||
| 299 | void setTransient(Transient annotation) { | ||
| 300 | if ((annotation != null) && (null == getValue(TRANSIENT))) { | ||
| 301 | setValue(TRANSIENT, annotation.value()); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | /** | ||
| 306 | * Indicates whether the feature is transient. | ||
| 307 | * | ||
| 308 | * @return {@code true} if the feature is transient, | ||
| 309 | * {@code false} otherwise | ||
| 310 | */ | ||
| 311 | boolean isTransient() { | ||
| 312 | Object value = getValue(TRANSIENT); | ||
| 313 | return (value instanceof Boolean) | ||
| 314 | ? (Boolean) value | ||
| 315 | : false; | ||
| 316 | } | ||
| 317 | |||
| 318 | // Package private methods for recreating the weak/soft referent | ||
| 319 | |||
| 320 | void setClass0(Class cls) { | ||
| 321 | this.classRef = getWeakReference(cls); | ||
| 322 | } | ||
| 323 | |||
| 324 | Class getClass0() { | ||
| 325 | return (this.classRef != null) | ||
| 326 | ? this.classRef.get() | ||
| 327 | : null; | ||
| 328 | } | ||
| 329 | |||
| 330 | /** | ||
| 331 | * Creates a new soft reference that refers to the given object. | ||
| 332 | * | ||
| 333 | * @return a new soft reference or <code>null</code> if object is <code>null</code> | ||
| 334 | * | ||
| 335 | * @see SoftReference | ||
| 336 | */ | ||
| 337 | static <T> Reference<T> getSoftReference(T object) { | ||
| 338 | return (object != null) | ||
| 339 | ? new SoftReference<T>(object) | ||
| 340 | : null; | ||
| 341 | } | ||
| 342 | |||
| 343 | /** | ||
| 344 | * Creates a new weak reference that refers to the given object. | ||
| 345 | * | ||
| 346 | * @return a new weak reference or <code>null</code> if object is <code>null</code> | ||
| 347 | * | ||
| 348 | * @see WeakReference | ||
| 349 | */ | ||
| 350 | static <T> Reference<T> getWeakReference(T object) { | ||
| 351 | return (object != null) | ||
| 352 | ? new WeakReference<T>(object) | ||
| 353 | : null; | ||
| 354 | } | ||
| 355 | |||
| 356 | /** | ||
| 357 | * Resolves the return type of the method. | ||
| 358 | * | ||
| 359 | * @param base the class that contains the method in the hierarchy | ||
| 360 | * @param method the object that represents the method | ||
| 361 | * @return a class identifying the return type of the method | ||
| 362 | * | ||
| 363 | * @see Method#getGenericReturnType | ||
| 364 | * @see Method#getReturnType | ||
| 365 | */ | ||
| 366 | static Class getReturnType(Class base, Method method) { | ||
| 367 | if (base == null) { | ||
| 368 | base = method.getDeclaringClass(); | ||
| 369 | } | ||
| 370 | return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericReturnType())); | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Resolves the parameter types of the method. | ||
| 375 | * | ||
| 376 | * @param base the class that contains the method in the hierarchy | ||
| 377 | * @param method the object that represents the method | ||
| 378 | * @return an array of classes identifying the parameter types of the method | ||
| 379 | * | ||
| 380 | * @see Method#getGenericParameterTypes | ||
| 381 | * @see Method#getParameterTypes | ||
| 382 | */ | ||
| 383 | static Class[] getParameterTypes(Class base, Method method) { | ||
| 384 | if (base == null) { | ||
| 385 | base = method.getDeclaringClass(); | ||
| 386 | } | ||
| 387 | return TypeResolver.erase(TypeResolver.resolveInClass(base, method.getGenericParameterTypes())); | ||
| 388 | } | ||
| 389 | |||
| 390 | private boolean expert; | ||
| 391 | private boolean hidden; | ||
| 392 | private boolean preferred; | ||
| 393 | private String shortDescription; | ||
| 394 | private String name; | ||
| 395 | private String displayName; | ||
| 396 | private Hashtable<String, Object> table; | ||
| 397 | |||
| 398 | /** | ||
| 399 | * Returns a string representation of the object. | ||
| 400 | * | ||
| 401 | * @return a string representation of the object | ||
| 402 | * | ||
| 403 | * @since 1.7 | ||
| 404 | */ | ||
| 405 | public String toString() { | ||
| 406 | StringBuilder sb = new StringBuilder(getClass().getName()); | ||
| 407 | sb.append("[name=").append(this.name); | ||
| 408 | appendTo(sb, "displayName", this.displayName); | ||
| 409 | appendTo(sb, "shortDescription", this.shortDescription); | ||
| 410 | appendTo(sb, "preferred", this.preferred); | ||
| 411 | appendTo(sb, "hidden", this.hidden); | ||
| 412 | appendTo(sb, "expert", this.expert); | ||
| 413 | if ((this.table != null) && !this.table.isEmpty()) { | ||
| 414 | sb.append("; values={"); | ||
| 415 | for (Entry<String, Object> entry : this.table.entrySet()) { | ||
| 416 | sb.append(entry.getKey()).append("=").append(entry.getValue()).append("; "); | ||
| 417 | } | ||
| 418 | sb.setLength(sb.length() - 2); | ||
| 419 | sb.append("}"); | ||
| 420 | } | ||
| 421 | appendTo(sb); | ||
| 422 | return sb.append("]").toString(); | ||
| 423 | } | ||
| 424 | |||
| 425 | void appendTo(StringBuilder sb) { | ||
| 426 | } | ||
| 427 | |||
| 428 | static void appendTo(StringBuilder sb, String name, Reference reference) { | ||
| 429 | if (reference != null) { | ||
| 430 | appendTo(sb, name, reference.get()); | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | static void appendTo(StringBuilder sb, String name, Object value) { | ||
| 435 | if (value != null) { | ||
| 436 | sb.append("; ").append(name).append("=").append(value); | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | static void appendTo(StringBuilder sb, String name, boolean value) { | ||
| 441 | if (value) { | ||
| 442 | sb.append("; ").append(name); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | } | ||
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,78 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * An "IndexedPropertyChange" event gets delivered whenever a component that | ||
| 29 | * conforms to the JavaBeans™ specification (a "bean") changes a bound | ||
| 30 | * indexed property. This class is an extension of <code>PropertyChangeEvent</code> | ||
| 31 | * but contains the index of the property that has changed. | ||
| 32 | * <P> | ||
| 33 | * Null values may be provided for the old and the new values if their | ||
| 34 | * true values are not known. | ||
| 35 | * <P> | ||
| 36 | * An event source may send a null object as the name to indicate that an | ||
| 37 | * arbitrary set of if its properties have changed. In this case the | ||
| 38 | * old and new values should also be null. | ||
| 39 | * | ||
| 40 | * @since 1.5 | ||
| 41 | * @author Mark Davidson | ||
| 42 | */ | ||
| 43 | public class IndexedPropertyChangeEvent extends PropertyChangeEvent { | ||
| 44 | private static final long serialVersionUID = -320227448495806870L; | ||
| 45 | |||
| 46 | private int index; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Constructs a new <code>IndexedPropertyChangeEvent</code> object. | ||
| 50 | * | ||
| 51 | * @param source The bean that fired the event. | ||
| 52 | * @param propertyName The programmatic name of the property that | ||
| 53 | * was changed. | ||
| 54 | * @param oldValue The old value of the property. | ||
| 55 | * @param newValue The new value of the property. | ||
| 56 | * @param index index of the property element that was changed. | ||
| 57 | */ | ||
| 58 | public IndexedPropertyChangeEvent(Object source, String propertyName, | ||
| 59 | Object oldValue, Object newValue, | ||
| 60 | int index) { | ||
| 61 | super (source, propertyName, oldValue, newValue); | ||
| 62 | this.index = index; | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Gets the index of the property that was changed. | ||
| 67 | * | ||
| 68 | * @return The index specifying the property element that was | ||
| 69 | * changed. | ||
| 70 | */ | ||
| 71 | public int getIndex() { | ||
| 72 | return index; | ||
| 73 | } | ||
| 74 | |||
| 75 | void appendTo(StringBuilder sb) { | ||
| 76 | sb.append("; index=").append(getIndex()); | ||
| 77 | } | ||
| 78 | } | ||
Binary file not shown.
| @@ -0,0 +1,523 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.lang.ref.Reference; | ||
| 29 | import java.lang.reflect.Method; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * An IndexedPropertyDescriptor describes a property that acts like an | ||
| 33 | * array and has an indexed read and/or indexed write method to access | ||
| 34 | * specific elements of the array. | ||
| 35 | * <p> | ||
| 36 | * An indexed property may also provide simple non-indexed read and write | ||
| 37 | * methods. If these are present, they read and write arrays of the type | ||
| 38 | * returned by the indexed read method. | ||
| 39 | */ | ||
| 40 | |||
| 41 | public class IndexedPropertyDescriptor extends PropertyDescriptor { | ||
| 42 | |||
| 43 | private Reference<Class> indexedPropertyTypeRef; | ||
| 44 | private Reference<Method> indexedReadMethodRef; | ||
| 45 | private Reference<Method> indexedWriteMethodRef; | ||
| 46 | |||
| 47 | private String indexedReadMethodName; | ||
| 48 | private String indexedWriteMethodName; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * This constructor constructs an IndexedPropertyDescriptor for a property | ||
| 52 | * that follows the standard Java conventions by having getFoo and setFoo | ||
| 53 | * accessor methods, for both indexed access and array access. | ||
| 54 | * <p> | ||
| 55 | * Thus if the argument name is "fred", it will assume that there | ||
| 56 | * is an indexed reader method "getFred", a non-indexed (array) reader | ||
| 57 | * method also called "getFred", an indexed writer method "setFred", | ||
| 58 | * and finally a non-indexed writer method "setFred". | ||
| 59 | * | ||
| 60 | * @param propertyName The programmatic name of the property. | ||
| 61 | * @param beanClass The Class object for the target bean. | ||
| 62 | * @exception IntrospectionException if an exception occurs during | ||
| 63 | * introspection. | ||
| 64 | */ | ||
| 65 | public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass) | ||
| 66 | throws IntrospectionException { | ||
| 67 | this(propertyName, beanClass, | ||
| 68 | Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName), | ||
| 69 | Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName), | ||
| 70 | Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName), | ||
| 71 | Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName)); | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * This constructor takes the name of a simple property, and method | ||
| 76 | * names for reading and writing the property, both indexed | ||
| 77 | * and non-indexed. | ||
| 78 | * | ||
| 79 | * @param propertyName The programmatic name of the property. | ||
| 80 | * @param beanClass The Class object for the target bean. | ||
| 81 | * @param readMethodName The name of the method used for reading the property | ||
| 82 | * values as an array. May be null if the property is write-only | ||
| 83 | * or must be indexed. | ||
| 84 | * @param writeMethodName The name of the method used for writing the property | ||
| 85 | * values as an array. May be null if the property is read-only | ||
| 86 | * or must be indexed. | ||
| 87 | * @param indexedReadMethodName The name of the method used for reading | ||
| 88 | * an indexed property value. | ||
| 89 | * May be null if the property is write-only. | ||
| 90 | * @param indexedWriteMethodName The name of the method used for writing | ||
| 91 | * an indexed property value. | ||
| 92 | * May be null if the property is read-only. | ||
| 93 | * @exception IntrospectionException if an exception occurs during | ||
| 94 | * introspection. | ||
| 95 | */ | ||
| 96 | public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass, | ||
| 97 | String readMethodName, String writeMethodName, | ||
| 98 | String indexedReadMethodName, String indexedWriteMethodName) | ||
| 99 | throws IntrospectionException { | ||
| 100 | super(propertyName, beanClass, readMethodName, writeMethodName); | ||
| 101 | |||
| 102 | this.indexedReadMethodName = indexedReadMethodName; | ||
| 103 | if (indexedReadMethodName != null && getIndexedReadMethod() == null) { | ||
| 104 | throw new IntrospectionException("Method not found: " + indexedReadMethodName); | ||
| 105 | } | ||
| 106 | |||
| 107 | this.indexedWriteMethodName = indexedWriteMethodName; | ||
| 108 | if (indexedWriteMethodName != null && getIndexedWriteMethod() == null) { | ||
| 109 | throw new IntrospectionException("Method not found: " + indexedWriteMethodName); | ||
| 110 | } | ||
| 111 | // Implemented only for type checking. | ||
| 112 | findIndexedPropertyType(getIndexedReadMethod(), getIndexedWriteMethod()); | ||
| 113 | } | ||
| 114 | |||
| 115 | /** | ||
| 116 | * This constructor takes the name of a simple property, and Method | ||
| 117 | * objects for reading and writing the property. | ||
| 118 | * | ||
| 119 | * @param propertyName The programmatic name of the property. | ||
| 120 | * @param readMethod The method used for reading the property values as an array. | ||
| 121 | * May be null if the property is write-only or must be indexed. | ||
| 122 | * @param writeMethod The method used for writing the property values as an array. | ||
| 123 | * May be null if the property is read-only or must be indexed. | ||
| 124 | * @param indexedReadMethod The method used for reading an indexed property value. | ||
| 125 | * May be null if the property is write-only. | ||
| 126 | * @param indexedWriteMethod The method used for writing an indexed property value. | ||
| 127 | * May be null if the property is read-only. | ||
| 128 | * @exception IntrospectionException if an exception occurs during | ||
| 129 | * introspection. | ||
| 130 | */ | ||
| 131 | public IndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod, | ||
| 132 | Method indexedReadMethod, Method indexedWriteMethod) | ||
| 133 | throws IntrospectionException { | ||
| 134 | super(propertyName, readMethod, writeMethod); | ||
| 135 | |||
| 136 | setIndexedReadMethod0(indexedReadMethod); | ||
| 137 | setIndexedWriteMethod0(indexedWriteMethod); | ||
| 138 | |||
| 139 | // Type checking | ||
| 140 | setIndexedPropertyType(findIndexedPropertyType(indexedReadMethod, indexedWriteMethod)); | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Creates <code>PropertyDescriptor</code> for the specified bean | ||
| 145 | * with the specified name and methods to read/write the property value. | ||
| 146 | * | ||
| 147 | * @param bean the type of the target bean | ||
| 148 | * @param base the base name of the property (the rest of the method name) | ||
| 149 | * @param read the method used for reading the property value | ||
| 150 | * @param write the method used for writing the property value | ||
| 151 | * @param readIndexed the method used for reading an indexed property value | ||
| 152 | * @param writeIndexed the method used for writing an indexed property value | ||
| 153 | * @exception IntrospectionException if an exception occurs during introspection | ||
| 154 | * | ||
| 155 | * @since 1.7 | ||
| 156 | */ | ||
| 157 | IndexedPropertyDescriptor(Class<?> bean, String base, Method read, Method write, Method readIndexed, Method writeIndexed) throws IntrospectionException { | ||
| 158 | super(bean, base, read, write); | ||
| 159 | |||
| 160 | setIndexedReadMethod0(readIndexed); | ||
| 161 | setIndexedWriteMethod0(writeIndexed); | ||
| 162 | |||
| 163 | // Type checking | ||
| 164 | setIndexedPropertyType(findIndexedPropertyType(readIndexed, writeIndexed)); | ||
| 165 | } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Gets the method that should be used to read an indexed | ||
| 169 | * property value. | ||
| 170 | * | ||
| 171 | * @return The method that should be used to read an indexed | ||
| 172 | * property value. | ||
| 173 | * May return null if the property isn't indexed or is write-only. | ||
| 174 | */ | ||
| 175 | public synchronized Method getIndexedReadMethod() { | ||
| 176 | Method indexedReadMethod = getIndexedReadMethod0(); | ||
| 177 | if (indexedReadMethod == null) { | ||
| 178 | Class cls = getClass0(); | ||
| 179 | if (cls == null || | ||
| 180 | (indexedReadMethodName == null && indexedReadMethodRef == null)) { | ||
| 181 | // the Indexed readMethod was explicitly set to null. | ||
| 182 | return null; | ||
| 183 | } | ||
| 184 | if (indexedReadMethodName == null) { | ||
| 185 | Class type = getIndexedPropertyType0(); | ||
| 186 | if (type == boolean.class || type == null) { | ||
| 187 | indexedReadMethodName = Introspector.IS_PREFIX + getBaseName(); | ||
| 188 | } else { | ||
| 189 | indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | Class[] args = { int.class }; | ||
| 194 | indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); | ||
| 195 | if (indexedReadMethod == null) { | ||
| 196 | // no "is" method, so look for a "get" method. | ||
| 197 | indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); | ||
| 198 | indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); | ||
| 199 | } | ||
| 200 | setIndexedReadMethod0(indexedReadMethod); | ||
| 201 | } | ||
| 202 | return indexedReadMethod; | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 206 | * Sets the method that should be used to read an indexed property value. | ||
| 207 | * | ||
| 208 | * @param readMethod The new indexed read method. | ||
| 209 | */ | ||
| 210 | public synchronized void setIndexedReadMethod(Method readMethod) | ||
| 211 | throws IntrospectionException { | ||
| 212 | |||
| 213 | // the indexed property type is set by the reader. | ||
| 214 | setIndexedPropertyType(findIndexedPropertyType(readMethod, | ||
| 215 | getIndexedWriteMethod0())); | ||
| 216 | setIndexedReadMethod0(readMethod); | ||
| 217 | } | ||
| 218 | |||
| 219 | private void setIndexedReadMethod0(Method readMethod) { | ||
| 220 | if (readMethod == null) { | ||
| 221 | indexedReadMethodName = null; | ||
| 222 | indexedReadMethodRef = null; | ||
| 223 | return; | ||
| 224 | } | ||
| 225 | setClass0(readMethod.getDeclaringClass()); | ||
| 226 | |||
| 227 | indexedReadMethodName = readMethod.getName(); | ||
| 228 | this.indexedReadMethodRef = getSoftReference(readMethod); | ||
| 229 | setTransient(readMethod.getAnnotation(Transient.class)); | ||
| 230 | } | ||
| 231 | |||
| 232 | |||
| 233 | /** | ||
| 234 | * Gets the method that should be used to write an indexed property value. | ||
| 235 | * | ||
| 236 | * @return The method that should be used to write an indexed | ||
| 237 | * property value. | ||
| 238 | * May return null if the property isn't indexed or is read-only. | ||
| 239 | */ | ||
| 240 | public synchronized Method getIndexedWriteMethod() { | ||
| 241 | Method indexedWriteMethod = getIndexedWriteMethod0(); | ||
| 242 | if (indexedWriteMethod == null) { | ||
| 243 | Class cls = getClass0(); | ||
| 244 | if (cls == null || | ||
| 245 | (indexedWriteMethodName == null && indexedWriteMethodRef == null)) { | ||
| 246 | // the Indexed writeMethod was explicitly set to null. | ||
| 247 | return null; | ||
| 248 | } | ||
| 249 | |||
| 250 | // We need the indexed type to ensure that we get the correct method. | ||
| 251 | // Cannot use the getIndexedPropertyType method since that could | ||
| 252 | // result in an infinite loop. | ||
| 253 | Class type = getIndexedPropertyType0(); | ||
| 254 | if (type == null) { | ||
| 255 | try { | ||
| 256 | type = findIndexedPropertyType(getIndexedReadMethod(), null); | ||
| 257 | setIndexedPropertyType(type); | ||
| 258 | } catch (IntrospectionException ex) { | ||
| 259 | // Set iprop type to be the classic type | ||
| 260 | Class propType = getPropertyType(); | ||
| 261 | if (propType.isArray()) { | ||
| 262 | type = propType.getComponentType(); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | if (indexedWriteMethodName == null) { | ||
| 268 | indexedWriteMethodName = Introspector.SET_PREFIX + getBaseName(); | ||
| 269 | } | ||
| 270 | |||
| 271 | Class[] args = (type == null) ? null : new Class[] { int.class, type }; | ||
| 272 | indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName, 2, args); | ||
| 273 | if (indexedWriteMethod != null) { | ||
| 274 | if (!indexedWriteMethod.getReturnType().equals(void.class)) { | ||
| 275 | indexedWriteMethod = null; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | setIndexedWriteMethod0(indexedWriteMethod); | ||
| 279 | } | ||
| 280 | return indexedWriteMethod; | ||
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * Sets the method that should be used to write an indexed property value. | ||
| 285 | * | ||
| 286 | * @param writeMethod The new indexed write method. | ||
| 287 | */ | ||
| 288 | public synchronized void setIndexedWriteMethod(Method writeMethod) | ||
| 289 | throws IntrospectionException { | ||
| 290 | |||
| 291 | // If the indexed property type has not been set, then set it. | ||
| 292 | Class type = findIndexedPropertyType(getIndexedReadMethod(), | ||
| 293 | writeMethod); | ||
| 294 | setIndexedPropertyType(type); | ||
| 295 | setIndexedWriteMethod0(writeMethod); | ||
| 296 | } | ||
| 297 | |||
| 298 | private void setIndexedWriteMethod0(Method writeMethod) { | ||
| 299 | if (writeMethod == null) { | ||
| 300 | indexedWriteMethodName = null; | ||
| 301 | indexedWriteMethodRef = null; | ||
| 302 | return; | ||
| 303 | } | ||
| 304 | setClass0(writeMethod.getDeclaringClass()); | ||
| 305 | |||
| 306 | indexedWriteMethodName = writeMethod.getName(); | ||
| 307 | this.indexedWriteMethodRef = getSoftReference(writeMethod); | ||
| 308 | setTransient(writeMethod.getAnnotation(Transient.class)); | ||
| 309 | } | ||
| 310 | |||
| 311 | /** | ||
| 312 | * Returns the Java type info for the indexed property. | ||
| 313 | * Note that the {@code Class} object may describe | ||
| 314 | * primitive Java types such as {@code int}. | ||
| 315 | * This type is returned by the indexed read method | ||
| 316 | * or is used as the parameter type of the indexed write method. | ||
| 317 | * | ||
| 318 | * @return the {@code Class} object that represents the Java type info, | ||
| 319 | * or {@code null} if the type cannot be determined | ||
| 320 | */ | ||
| 321 | public synchronized Class<?> getIndexedPropertyType() { | ||
| 322 | Class type = getIndexedPropertyType0(); | ||
| 323 | if (type == null) { | ||
| 324 | try { | ||
| 325 | type = findIndexedPropertyType(getIndexedReadMethod(), | ||
| 326 | getIndexedWriteMethod()); | ||
| 327 | setIndexedPropertyType(type); | ||
| 328 | } catch (IntrospectionException ex) { | ||
| 329 | // fall | ||
| 330 | } | ||
| 331 | } | ||
| 332 | return type; | ||
| 333 | } | ||
| 334 | |||
| 335 | // Private methods which set get/set the Reference objects | ||
| 336 | |||
| 337 | private void setIndexedPropertyType(Class type) { | ||
| 338 | this.indexedPropertyTypeRef = getWeakReference(type); | ||
| 339 | } | ||
| 340 | |||
| 341 | private Class getIndexedPropertyType0() { | ||
| 342 | return (this.indexedPropertyTypeRef != null) | ||
| 343 | ? this.indexedPropertyTypeRef.get() | ||
| 344 | : null; | ||
| 345 | } | ||
| 346 | |||
| 347 | private Method getIndexedReadMethod0() { | ||
| 348 | return (this.indexedReadMethodRef != null) | ||
| 349 | ? this.indexedReadMethodRef.get() | ||
| 350 | : null; | ||
| 351 | } | ||
| 352 | |||
| 353 | private Method getIndexedWriteMethod0() { | ||
| 354 | return (this.indexedWriteMethodRef != null) | ||
| 355 | ? this.indexedWriteMethodRef.get() | ||
| 356 | : null; | ||
| 357 | } | ||
| 358 | |||
| 359 | private Class findIndexedPropertyType(Method indexedReadMethod, | ||
| 360 | Method indexedWriteMethod) | ||
| 361 | throws IntrospectionException { | ||
| 362 | Class indexedPropertyType = null; | ||
| 363 | |||
| 364 | if (indexedReadMethod != null) { | ||
| 365 | Class params[] = getParameterTypes(getClass0(), indexedReadMethod); | ||
| 366 | if (params.length != 1) { | ||
| 367 | throw new IntrospectionException("bad indexed read method arg count"); | ||
| 368 | } | ||
| 369 | if (params[0] != Integer.TYPE) { | ||
| 370 | throw new IntrospectionException("non int index to indexed read method"); | ||
| 371 | } | ||
| 372 | indexedPropertyType = getReturnType(getClass0(), indexedReadMethod); | ||
| 373 | if (indexedPropertyType == Void.TYPE) { | ||
| 374 | throw new IntrospectionException("indexed read method returns void"); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | if (indexedWriteMethod != null) { | ||
| 378 | Class params[] = getParameterTypes(getClass0(), indexedWriteMethod); | ||
| 379 | if (params.length != 2) { | ||
| 380 | throw new IntrospectionException("bad indexed write method arg count"); | ||
| 381 | } | ||
| 382 | if (params[0] != Integer.TYPE) { | ||
| 383 | throw new IntrospectionException("non int index to indexed write method"); | ||
| 384 | } | ||
| 385 | if (indexedPropertyType != null && indexedPropertyType != params[1]) { | ||
| 386 | throw new IntrospectionException( | ||
| 387 | "type mismatch between indexed read and indexed write methods: " | ||
| 388 | + getName()); | ||
| 389 | } | ||
| 390 | indexedPropertyType = params[1]; | ||
| 391 | } | ||
| 392 | Class propertyType = getPropertyType(); | ||
| 393 | if (propertyType != null && (!propertyType.isArray() || | ||
| 394 | propertyType.getComponentType() != indexedPropertyType)) { | ||
| 395 | throw new IntrospectionException("type mismatch between indexed and non-indexed methods: " | ||
| 396 | + getName()); | ||
| 397 | } | ||
| 398 | return indexedPropertyType; | ||
| 399 | } | ||
| 400 | |||
| 401 | /** | ||
| 402 | * Compares this <code>PropertyDescriptor</code> against the specified object. | ||
| 403 | * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s | ||
| 404 | * are the same if the read, write, property types, property editor and | ||
| 405 | * flags are equivalent. | ||
| 406 | * | ||
| 407 | * @since 1.4 | ||
| 408 | */ | ||
| 409 | public boolean equals(Object obj) { | ||
| 410 | // Note: This would be identical to PropertyDescriptor but they don't | ||
| 411 | // share the same fields. | ||
| 412 | if (this == obj) { | ||
| 413 | return true; | ||
| 414 | } | ||
| 415 | |||
| 416 | if (obj != null && obj instanceof IndexedPropertyDescriptor) { | ||
| 417 | IndexedPropertyDescriptor other = (IndexedPropertyDescriptor)obj; | ||
| 418 | Method otherIndexedReadMethod = other.getIndexedReadMethod(); | ||
| 419 | Method otherIndexedWriteMethod = other.getIndexedWriteMethod(); | ||
| 420 | |||
| 421 | if (!compareMethods(getIndexedReadMethod(), otherIndexedReadMethod)) { | ||
| 422 | return false; | ||
| 423 | } | ||
| 424 | |||
| 425 | if (!compareMethods(getIndexedWriteMethod(), otherIndexedWriteMethod)) { | ||
| 426 | return false; | ||
| 427 | } | ||
| 428 | |||
| 429 | if (getIndexedPropertyType() != other.getIndexedPropertyType()) { | ||
| 430 | return false; | ||
| 431 | } | ||
| 432 | return super.equals(obj); | ||
| 433 | } | ||
| 434 | return false; | ||
| 435 | } | ||
| 436 | |||
| 437 | /** | ||
| 438 | * Package-private constructor. | ||
| 439 | * Merge two property descriptors. Where they conflict, give the | ||
| 440 | * second argument (y) priority over the first argumnnt (x). | ||
| 441 | * | ||
| 442 | * @param x The first (lower priority) PropertyDescriptor | ||
| 443 | * @param y The second (higher priority) PropertyDescriptor | ||
| 444 | */ | ||
| 445 | |||
| 446 | IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) { | ||
| 447 | super(x,y); | ||
| 448 | if (x instanceof IndexedPropertyDescriptor) { | ||
| 449 | IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor)x; | ||
| 450 | try { | ||
| 451 | Method xr = ix.getIndexedReadMethod(); | ||
| 452 | if (xr != null) { | ||
| 453 | setIndexedReadMethod(xr); | ||
| 454 | } | ||
| 455 | |||
| 456 | Method xw = ix.getIndexedWriteMethod(); | ||
| 457 | if (xw != null) { | ||
| 458 | setIndexedWriteMethod(xw); | ||
| 459 | } | ||
| 460 | } catch (IntrospectionException ex) { | ||
| 461 | // Should not happen | ||
| 462 | throw new AssertionError(ex); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | if (y instanceof IndexedPropertyDescriptor) { | ||
| 466 | IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor)y; | ||
| 467 | try { | ||
| 468 | Method yr = iy.getIndexedReadMethod(); | ||
| 469 | if (yr != null && yr.getDeclaringClass() == getClass0()) { | ||
| 470 | setIndexedReadMethod(yr); | ||
| 471 | } | ||
| 472 | |||
| 473 | Method yw = iy.getIndexedWriteMethod(); | ||
| 474 | if (yw != null && yw.getDeclaringClass() == getClass0()) { | ||
| 475 | setIndexedWriteMethod(yw); | ||
| 476 | } | ||
| 477 | } catch (IntrospectionException ex) { | ||
| 478 | // Should not happen | ||
| 479 | throw new AssertionError(ex); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | /* | ||
| 485 | * Package-private dup constructor | ||
| 486 | * This must isolate the new object from any changes to the old object. | ||
| 487 | */ | ||
| 488 | IndexedPropertyDescriptor(IndexedPropertyDescriptor old) { | ||
| 489 | super(old); | ||
| 490 | indexedReadMethodRef = old.indexedReadMethodRef; | ||
| 491 | indexedWriteMethodRef = old.indexedWriteMethodRef; | ||
| 492 | indexedPropertyTypeRef = old.indexedPropertyTypeRef; | ||
| 493 | indexedWriteMethodName = old.indexedWriteMethodName; | ||
| 494 | indexedReadMethodName = old.indexedReadMethodName; | ||
| 495 | } | ||
| 496 | |||
| 497 | /** | ||
| 498 | * Returns a hash code value for the object. | ||
| 499 | * See {@link java.lang.Object#hashCode} for a complete description. | ||
| 500 | * | ||
| 501 | * @return a hash code value for this object. | ||
| 502 | * @since 1.5 | ||
| 503 | */ | ||
| 504 | public int hashCode() { | ||
| 505 | int result = super.hashCode(); | ||
| 506 | |||
| 507 | result = 37 * result + ((indexedWriteMethodName == null) ? 0 : | ||
| 508 | indexedWriteMethodName.hashCode()); | ||
| 509 | result = 37 * result + ((indexedReadMethodName == null) ? 0 : | ||
| 510 | indexedReadMethodName.hashCode()); | ||
| 511 | result = 37 * result + ((getIndexedPropertyType() == null) ? 0 : | ||
| 512 | getIndexedPropertyType().hashCode()); | ||
| 513 | |||
| 514 | return result; | ||
| 515 | } | ||
| 516 | |||
| 517 | void appendTo(StringBuilder sb) { | ||
| 518 | super.appendTo(sb); | ||
| 519 | appendTo(sb, "indexedPropertyType", this.indexedPropertyTypeRef); | ||
| 520 | appendTo(sb, "indexedReadMethod", this.indexedReadMethodRef); | ||
| 521 | appendTo(sb, "indexedWriteMethod", this.indexedWriteMethodRef); | ||
| 522 | } | ||
| 523 | } | ||
Binary file not shown.
| @@ -0,0 +1,50 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Thrown when an exception happens during Introspection. | ||
| 30 | * <p> | ||
| 31 | * Typical causes include not being able to map a string class name | ||
| 32 | * to a Class object, not being able to resolve a string method name, | ||
| 33 | * or specifying a method name that has the wrong type signature for | ||
| 34 | * its intended use. | ||
| 35 | */ | ||
| 36 | |||
| 37 | public | ||
| 38 | class IntrospectionException extends Exception { | ||
| 39 | private static final long serialVersionUID = -3728150539969542619L; | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Constructs an <code>IntrospectionException</code> with a | ||
| 43 | * detailed message. | ||
| 44 | * | ||
| 45 | * @param mess Descriptive message | ||
| 46 | */ | ||
| 47 | public IntrospectionException(String mess) { | ||
| 48 | super(mess); | ||
| 49 | } | ||
| 50 | } | ||
Binary file not shown.
| @@ -0,0 +1,1558 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import com.sun.beans.WeakCache; | ||
| 29 | import com.sun.beans.finder.BeanInfoFinder; | ||
| 30 | import com.sun.beans.finder.ClassFinder; | ||
| 31 | |||
| 32 | import java.awt.Component; | ||
| 33 | |||
| 34 | import java.lang.ref.Reference; | ||
| 35 | import java.lang.ref.SoftReference; | ||
| 36 | import java.lang.reflect.Method; | ||
| 37 | import java.lang.reflect.Modifier; | ||
| 38 | |||
| 39 | import java.util.Map; | ||
| 40 | import java.util.ArrayList; | ||
| 41 | import java.util.HashMap; | ||
| 42 | import java.util.Iterator; | ||
| 43 | import java.util.EventListener; | ||
| 44 | import java.util.EventObject; | ||
| 45 | import java.util.List; | ||
| 46 | import java.util.TreeMap; | ||
| 47 | import java.util.WeakHashMap; | ||
| 48 | |||
| 49 | import sun.awt.AppContext; | ||
| 50 | import sun.reflect.misc.ReflectUtil; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * The Introspector class provides a standard way for tools to learn about | ||
| 54 | * the properties, events, and methods supported by a target Java Bean. | ||
| 55 | * <p> | ||
| 56 | * For each of those three kinds of information, the Introspector will | ||
| 57 | * separately analyze the bean's class and superclasses looking for | ||
| 58 | * either explicit or implicit information and use that information to | ||
| 59 | * build a BeanInfo object that comprehensively describes the target bean. | ||
| 60 | * <p> | ||
| 61 | * For each class "Foo", explicit information may be available if there exists | ||
| 62 | * a corresponding "FooBeanInfo" class that provides a non-null value when | ||
| 63 | * queried for the information. We first look for the BeanInfo class by | ||
| 64 | * taking the full package-qualified name of the target bean class and | ||
| 65 | * appending "BeanInfo" to form a new class name. If this fails, then | ||
| 66 | * we take the final classname component of this name, and look for that | ||
| 67 | * class in each of the packages specified in the BeanInfo package search | ||
| 68 | * path. | ||
| 69 | * <p> | ||
| 70 | * Thus for a class such as "sun.xyz.OurButton" we would first look for a | ||
| 71 | * BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd | ||
| 72 | * look in each package in the BeanInfo search path for an OurButtonBeanInfo | ||
| 73 | * class. With the default search path, this would mean looking for | ||
| 74 | * "sun.beans.infos.OurButtonBeanInfo". | ||
| 75 | * <p> | ||
| 76 | * If a class provides explicit BeanInfo about itself then we add that to | ||
| 77 | * the BeanInfo information we obtained from analyzing any derived classes, | ||
| 78 | * but we regard the explicit information as being definitive for the current | ||
| 79 | * class and its base classes, and do not proceed any further up the superclass | ||
| 80 | * chain. | ||
| 81 | * <p> | ||
| 82 | * If we don't find explicit BeanInfo on a class, we use low-level | ||
| 83 | * reflection to study the methods of the class and apply standard design | ||
| 84 | * patterns to identify property accessors, event sources, or public | ||
| 85 | * methods. We then proceed to analyze the class's superclass and add | ||
| 86 | * in the information from it (and possibly on up the superclass chain). | ||
| 87 | * <p> | ||
| 88 | * For more information about introspection and design patterns, please | ||
| 89 | * consult the | ||
| 90 | * <a href="http://java.sun.com/products/javabeans/docs/index.html">JavaBeans™ specification</a>. | ||
| 91 | */ | ||
| 92 | |||
| 93 | public class Introspector { | ||
| 94 | |||
| 95 | // Flags that can be used to control getBeanInfo: | ||
| 96 | public final static int USE_ALL_BEANINFO = 1; | ||
| 97 | public final static int IGNORE_IMMEDIATE_BEANINFO = 2; | ||
| 98 | public final static int IGNORE_ALL_BEANINFO = 3; | ||
| 99 | |||
| 100 | // Static Caches to speed up introspection. | ||
| 101 | private static WeakCache<Class<?>, Method[]> declaredMethodCache = | ||
| 102 | new WeakCache<Class<?>, Method[]>(); | ||
| 103 | |||
| 104 | private static final Object BEANINFO_CACHE = new Object(); | ||
| 105 | |||
| 106 | private Class beanClass; | ||
| 107 | private BeanInfo explicitBeanInfo; | ||
| 108 | private BeanInfo superBeanInfo; | ||
| 109 | private BeanInfo additionalBeanInfo[]; | ||
| 110 | |||
| 111 | private boolean propertyChangeSource = false; | ||
| 112 | private static Class eventListenerType = EventListener.class; | ||
| 113 | |||
| 114 | // These should be removed. | ||
| 115 | private String defaultEventName; | ||
| 116 | private String defaultPropertyName; | ||
| 117 | private int defaultEventIndex = -1; | ||
| 118 | private int defaultPropertyIndex = -1; | ||
| 119 | |||
| 120 | // Methods maps from Method objects to MethodDescriptors | ||
| 121 | private Map methods; | ||
| 122 | |||
| 123 | // properties maps from String names to PropertyDescriptors | ||
| 124 | private Map properties; | ||
| 125 | |||
| 126 | // events maps from String names to EventSetDescriptors | ||
| 127 | private Map events; | ||
| 128 | |||
| 129 | private final static EventSetDescriptor[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor[0]; | ||
| 130 | |||
| 131 | static final String ADD_PREFIX = "add"; | ||
| 132 | static final String REMOVE_PREFIX = "remove"; | ||
| 133 | static final String GET_PREFIX = "get"; | ||
| 134 | static final String SET_PREFIX = "set"; | ||
| 135 | static final String IS_PREFIX = "is"; | ||
| 136 | |||
| 137 | private static final Object FINDER_KEY = new Object(); | ||
| 138 | |||
| 139 | //====================================================================== | ||
| 140 | // Public methods | ||
| 141 | //====================================================================== | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Introspect on a Java Bean and learn about all its properties, exposed | ||
| 145 | * methods, and events. | ||
| 146 | * <p> | ||
| 147 | * If the BeanInfo class for a Java Bean has been previously Introspected | ||
| 148 | * then the BeanInfo class is retrieved from the BeanInfo cache. | ||
| 149 | * | ||
| 150 | * @param beanClass The bean class to be analyzed. | ||
| 151 | * @return A BeanInfo object describing the target bean. | ||
| 152 | * @exception IntrospectionException if an exception occurs during | ||
| 153 | * introspection. | ||
| 154 | * @see #flushCaches | ||
| 155 | * @see #flushFromCaches | ||
| 156 | */ | ||
| 157 | public static BeanInfo getBeanInfo(Class<?> beanClass) | ||
| 158 | throws IntrospectionException | ||
| 159 | { | ||
| 160 | if (!ReflectUtil.isPackageAccessible(beanClass)) { | ||
| 161 | return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); | ||
| 162 | } | ||
| 163 | Map<Class<?>, BeanInfo> beanInfoCache; | ||
| 164 | BeanInfo beanInfo; | ||
| 165 | synchronized (BEANINFO_CACHE) { | ||
| 166 | beanInfoCache = (Map<Class<?>, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE); | ||
| 167 | if (beanInfoCache == null) { | ||
| 168 | beanInfoCache = new WeakHashMap<Class<?>, BeanInfo>(); | ||
| 169 | AppContext.getAppContext().put(BEANINFO_CACHE, beanInfoCache); | ||
| 170 | } | ||
| 171 | beanInfo = beanInfoCache.get(beanClass); | ||
| 172 | } | ||
| 173 | if (beanInfo == null) { | ||
| 174 | beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo(); | ||
| 175 | synchronized (BEANINFO_CACHE) { | ||
| 176 | beanInfoCache.put(beanClass, beanInfo); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | return beanInfo; | ||
| 180 | } | ||
| 181 | |||
| 182 | /** | ||
| 183 | * Introspect on a Java bean and learn about all its properties, exposed | ||
| 184 | * methods, and events, subject to some control flags. | ||
| 185 | * <p> | ||
| 186 | * If the BeanInfo class for a Java Bean has been previously Introspected | ||
| 187 | * based on the same arguments then the BeanInfo class is retrieved | ||
| 188 | * from the BeanInfo cache. | ||
| 189 | * | ||
| 190 | * @param beanClass The bean class to be analyzed. | ||
| 191 | * @param flags Flags to control the introspection. | ||
| 192 | * If flags == USE_ALL_BEANINFO then we use all of the BeanInfo | ||
| 193 | * classes we can discover. | ||
| 194 | * If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any | ||
| 195 | * BeanInfo associated with the specified beanClass. | ||
| 196 | * If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo | ||
| 197 | * associated with the specified beanClass or any of its | ||
| 198 | * parent classes. | ||
| 199 | * @return A BeanInfo object describing the target bean. | ||
| 200 | * @exception IntrospectionException if an exception occurs during | ||
| 201 | * introspection. | ||
| 202 | */ | ||
| 203 | public static BeanInfo getBeanInfo(Class<?> beanClass, int flags) | ||
| 204 | throws IntrospectionException { | ||
| 205 | return getBeanInfo(beanClass, null, flags); | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * Introspect on a Java bean and learn all about its properties, exposed | ||
| 210 | * methods, below a given "stop" point. | ||
| 211 | * <p> | ||
| 212 | * If the BeanInfo class for a Java Bean has been previously Introspected | ||
| 213 | * based on the same arguments, then the BeanInfo class is retrieved | ||
| 214 | * from the BeanInfo cache. | ||
| 215 | * | ||
| 216 | * @param beanClass The bean class to be analyzed. | ||
| 217 | * @param stopClass The baseclass at which to stop the analysis. Any | ||
| 218 | * methods/properties/events in the stopClass or in its baseclasses | ||
| 219 | * will be ignored in the analysis. | ||
| 220 | * @exception IntrospectionException if an exception occurs during | ||
| 221 | * introspection. | ||
| 222 | */ | ||
| 223 | public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass) | ||
| 224 | throws IntrospectionException { | ||
| 225 | return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO); | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Introspect on a Java Bean and learn about all its properties, | ||
| 230 | * exposed methods and events, below a given {@code stopClass} point | ||
| 231 | * subject to some control {@code flags}. | ||
| 232 | * <dl> | ||
| 233 | * <dt>USE_ALL_BEANINFO</dt> | ||
| 234 | * <dd>Any BeanInfo that can be discovered will be used.</dd> | ||
| 235 | * <dt>IGNORE_IMMEDIATE_BEANINFO</dt> | ||
| 236 | * <dd>Any BeanInfo associated with the specified {@code beanClass} will be ignored.</dd> | ||
| 237 | * <dt>IGNORE_ALL_BEANINFO</dt> | ||
| 238 | * <dd>Any BeanInfo associated with the specified {@code beanClass} | ||
| 239 | * or any of its parent classes will be ignored.</dd> | ||
| 240 | * </dl> | ||
| 241 | * Any methods/properties/events in the {@code stopClass} | ||
| 242 | * or in its parent classes will be ignored in the analysis. | ||
| 243 | * <p> | ||
| 244 | * If the BeanInfo class for a Java Bean has been | ||
| 245 | * previously introspected based on the same arguments then | ||
| 246 | * the BeanInfo class is retrieved from the BeanInfo cache. | ||
| 247 | * | ||
| 248 | * @param beanClass the bean class to be analyzed | ||
| 249 | * @param stopClass the parent class at which to stop the analysis | ||
| 250 | * @param flags flags to control the introspection | ||
| 251 | * @return a BeanInfo object describing the target bean | ||
| 252 | * @exception IntrospectionException if an exception occurs during introspection | ||
| 253 | * | ||
| 254 | * @since 1.7 | ||
| 255 | */ | ||
| 256 | public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass, | ||
| 257 | int flags) throws IntrospectionException { | ||
| 258 | BeanInfo bi; | ||
| 259 | if (stopClass == null && flags == USE_ALL_BEANINFO) { | ||
| 260 | // Same parameters to take advantage of caching. | ||
| 261 | bi = getBeanInfo(beanClass); | ||
| 262 | } else { | ||
| 263 | bi = (new Introspector(beanClass, stopClass, flags)).getBeanInfo(); | ||
| 264 | } | ||
| 265 | return bi; | ||
| 266 | |||
| 267 | // Old behaviour: Make an independent copy of the BeanInfo. | ||
| 268 | //return new GenericBeanInfo(bi); | ||
| 269 | } | ||
| 270 | |||
| 271 | |||
| 272 | /** | ||
| 273 | * Utility method to take a string and convert it to normal Java variable | ||
| 274 | * name capitalization. This normally means converting the first | ||
| 275 | * character from upper case to lower case, but in the (unusual) special | ||
| 276 | * case when there is more than one character and both the first and | ||
| 277 | * second characters are upper case, we leave it alone. | ||
| 278 | * <p> | ||
| 279 | * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays | ||
| 280 | * as "URL". | ||
| 281 | * | ||
| 282 | * @param name The string to be decapitalized. | ||
| 283 | * @return The decapitalized version of the string. | ||
| 284 | */ | ||
| 285 | public static String decapitalize(String name) { | ||
| 286 | if (name == null || name.length() == 0) { | ||
| 287 | return name; | ||
| 288 | } | ||
| 289 | if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && | ||
| 290 | Character.isUpperCase(name.charAt(0))){ | ||
| 291 | return name; | ||
| 292 | } | ||
| 293 | char chars[] = name.toCharArray(); | ||
| 294 | chars[0] = Character.toLowerCase(chars[0]); | ||
| 295 | return new String(chars); | ||
| 296 | } | ||
| 297 | |||
| 298 | /** | ||
| 299 | * Gets the list of package names that will be used for | ||
| 300 | * finding BeanInfo classes. | ||
| 301 | * | ||
| 302 | * @return The array of package names that will be searched in | ||
| 303 | * order to find BeanInfo classes. The default value | ||
| 304 | * for this array is implementation-dependent; e.g. | ||
| 305 | * Sun implementation initially sets to {"sun.beans.infos"}. | ||
| 306 | */ | ||
| 307 | |||
| 308 | public static String[] getBeanInfoSearchPath() { | ||
| 309 | return getFinder().getPackages(); | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * Change the list of package names that will be used for | ||
| 314 | * finding BeanInfo classes. The behaviour of | ||
| 315 | * this method is undefined if parameter path | ||
| 316 | * is null. | ||
| 317 | * | ||
| 318 | * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code> | ||
| 319 | * method is called. This could result in a SecurityException. | ||
| 320 | * | ||
| 321 | * @param path Array of package names. | ||
| 322 | * @exception SecurityException if a security manager exists and its | ||
| 323 | * <code>checkPropertiesAccess</code> method doesn't allow setting | ||
| 324 | * of system properties. | ||
| 325 | * @see SecurityManager#checkPropertiesAccess | ||
| 326 | */ | ||
| 327 | |||
| 328 | public static void setBeanInfoSearchPath(String[] path) { | ||
| 329 | SecurityManager sm = System.getSecurityManager(); | ||
| 330 | if (sm != null) { | ||
| 331 | sm.checkPropertiesAccess(); | ||
| 332 | } | ||
| 333 | getFinder().setPackages(path); | ||
| 334 | } | ||
| 335 | |||
| 336 | |||
| 337 | /** | ||
| 338 | * Flush all of the Introspector's internal caches. This method is | ||
| 339 | * not normally required. It is normally only needed by advanced | ||
| 340 | * tools that update existing "Class" objects in-place and need | ||
| 341 | * to make the Introspector re-analyze existing Class objects. | ||
| 342 | */ | ||
| 343 | |||
| 344 | public static void flushCaches() { | ||
| 345 | synchronized (BEANINFO_CACHE) { | ||
| 346 | Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); | ||
| 347 | if (beanInfoCache != null) { | ||
| 348 | beanInfoCache.clear(); | ||
| 349 | } | ||
| 350 | declaredMethodCache.clear(); | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | /** | ||
| 355 | * Flush the Introspector's internal cached information for a given class. | ||
| 356 | * This method is not normally required. It is normally only needed | ||
| 357 | * by advanced tools that update existing "Class" objects in-place | ||
| 358 | * and need to make the Introspector re-analyze an existing Class object. | ||
| 359 | * | ||
| 360 | * Note that only the direct state associated with the target Class | ||
| 361 | * object is flushed. We do not flush state for other Class objects | ||
| 362 | * with the same name, nor do we flush state for any related Class | ||
| 363 | * objects (such as subclasses), even though their state may include | ||
| 364 | * information indirectly obtained from the target Class object. | ||
| 365 | * | ||
| 366 | * @param clz Class object to be flushed. | ||
| 367 | * @throws NullPointerException If the Class object is null. | ||
| 368 | */ | ||
| 369 | public static void flushFromCaches(Class<?> clz) { | ||
| 370 | if (clz == null) { | ||
| 371 | throw new NullPointerException(); | ||
| 372 | } | ||
| 373 | synchronized (BEANINFO_CACHE) { | ||
| 374 | Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE); | ||
| 375 | if (beanInfoCache != null) { | ||
| 376 | beanInfoCache.put(clz, null); | ||
| 377 | } | ||
| 378 | declaredMethodCache.put(clz, null); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | //====================================================================== | ||
| 383 | // Private implementation methods | ||
| 384 | //====================================================================== | ||
| 385 | |||
| 386 | private Introspector(Class beanClass, Class stopClass, int flags) | ||
| 387 | throws IntrospectionException { | ||
| 388 | this.beanClass = beanClass; | ||
| 389 | |||
| 390 | // Check stopClass is a superClass of startClass. | ||
| 391 | if (stopClass != null) { | ||
| 392 | boolean isSuper = false; | ||
| 393 | for (Class c = beanClass.getSuperclass(); c != null; c = c.getSuperclass()) { | ||
| 394 | if (c == stopClass) { | ||
| 395 | isSuper = true; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | if (!isSuper) { | ||
| 399 | throw new IntrospectionException(stopClass.getName() + " not superclass of " + | ||
| 400 | beanClass.getName()); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | if (flags == USE_ALL_BEANINFO) { | ||
| 405 | explicitBeanInfo = findExplicitBeanInfo(beanClass); | ||
| 406 | } | ||
| 407 | |||
| 408 | Class superClass = beanClass.getSuperclass(); | ||
| 409 | if (superClass != stopClass) { | ||
| 410 | int newFlags = flags; | ||
| 411 | if (newFlags == IGNORE_IMMEDIATE_BEANINFO) { | ||
| 412 | newFlags = USE_ALL_BEANINFO; | ||
| 413 | } | ||
| 414 | superBeanInfo = getBeanInfo(superClass, stopClass, newFlags); | ||
| 415 | } | ||
| 416 | if (explicitBeanInfo != null) { | ||
| 417 | additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo(); | ||
| 418 | } | ||
| 419 | if (additionalBeanInfo == null) { | ||
| 420 | additionalBeanInfo = new BeanInfo[0]; | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | /** | ||
| 425 | * Constructs a GenericBeanInfo class from the state of the Introspector | ||
| 426 | */ | ||
| 427 | private BeanInfo getBeanInfo() throws IntrospectionException { | ||
| 428 | |||
| 429 | // the evaluation order here is import, as we evaluate the | ||
| 430 | // event sets and locate PropertyChangeListeners before we | ||
| 431 | // look for properties. | ||
| 432 | BeanDescriptor bd = getTargetBeanDescriptor(); | ||
| 433 | MethodDescriptor mds[] = getTargetMethodInfo(); | ||
| 434 | EventSetDescriptor esds[] = getTargetEventInfo(); | ||
| 435 | PropertyDescriptor pds[] = getTargetPropertyInfo(); | ||
| 436 | |||
| 437 | int defaultEvent = getTargetDefaultEventIndex(); | ||
| 438 | int defaultProperty = getTargetDefaultPropertyIndex(); | ||
| 439 | |||
| 440 | return new GenericBeanInfo(bd, esds, defaultEvent, pds, | ||
| 441 | defaultProperty, mds, explicitBeanInfo); | ||
| 442 | |||
| 443 | } | ||
| 444 | |||
| 445 | /** | ||
| 446 | * Looks for an explicit BeanInfo class that corresponds to the Class. | ||
| 447 | * First it looks in the existing package that the Class is defined in, | ||
| 448 | * then it checks to see if the class is its own BeanInfo. Finally, | ||
| 449 | * the BeanInfo search path is prepended to the class and searched. | ||
| 450 | * | ||
| 451 | * @param beanClass the class type of the bean | ||
| 452 | * @return Instance of an explicit BeanInfo class or null if one isn't found. | ||
| 453 | */ | ||
| 454 | private static BeanInfo findExplicitBeanInfo(Class beanClass) { | ||
| 455 | return getFinder().find(beanClass); | ||
| 456 | } | ||
| 457 | |||
| 458 | /** | ||
| 459 | * @return An array of PropertyDescriptors describing the editable | ||
| 460 | * properties supported by the target bean. | ||
| 461 | */ | ||
| 462 | |||
| 463 | private PropertyDescriptor[] getTargetPropertyInfo() { | ||
| 464 | |||
| 465 | // Check if the bean has its own BeanInfo that will provide | ||
| 466 | // explicit information. | ||
| 467 | PropertyDescriptor[] explicitProperties = null; | ||
| 468 | if (explicitBeanInfo != null) { | ||
| 469 | explicitProperties = getPropertyDescriptors(this.explicitBeanInfo); | ||
| 470 | } | ||
| 471 | |||
| 472 | if (explicitProperties == null && superBeanInfo != null) { | ||
| 473 | // We have no explicit BeanInfo properties. Check with our parent. | ||
| 474 | addPropertyDescriptors(getPropertyDescriptors(this.superBeanInfo)); | ||
| 475 | } | ||
| 476 | |||
| 477 | for (int i = 0; i < additionalBeanInfo.length; i++) { | ||
| 478 | addPropertyDescriptors(additionalBeanInfo[i].getPropertyDescriptors()); | ||
| 479 | } | ||
| 480 | |||
| 481 | if (explicitProperties != null) { | ||
| 482 | // Add the explicit BeanInfo data to our results. | ||
| 483 | addPropertyDescriptors(explicitProperties); | ||
| 484 | |||
| 485 | } else { | ||
| 486 | |||
| 487 | // Apply some reflection to the current class. | ||
| 488 | |||
| 489 | // First get an array of all the public methods at this level | ||
| 490 | Method methodList[] = getPublicDeclaredMethods(beanClass); | ||
| 491 | |||
| 492 | // Now analyze each method. | ||
| 493 | for (int i = 0; i < methodList.length; i++) { | ||
| 494 | Method method = methodList[i]; | ||
| 495 | if (method == null || method.isSynthetic()) { | ||
| 496 | continue; | ||
| 497 | } | ||
| 498 | // skip static methods. | ||
| 499 | int mods = method.getModifiers(); | ||
| 500 | if (Modifier.isStatic(mods)) { | ||
| 501 | continue; | ||
| 502 | } | ||
| 503 | String name = method.getName(); | ||
| 504 | Class argTypes[] = method.getParameterTypes(); | ||
| 505 | Class resultType = method.getReturnType(); | ||
| 506 | int argCount = argTypes.length; | ||
| 507 | PropertyDescriptor pd = null; | ||
| 508 | |||
| 509 | if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) { | ||
| 510 | // Optimization. Don't bother with invalid propertyNames. | ||
| 511 | continue; | ||
| 512 | } | ||
| 513 | |||
| 514 | try { | ||
| 515 | |||
| 516 | if (argCount == 0) { | ||
| 517 | if (name.startsWith(GET_PREFIX)) { | ||
| 518 | // Simple getter | ||
| 519 | pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null); | ||
| 520 | } else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) { | ||
| 521 | // Boolean getter | ||
| 522 | pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null); | ||
| 523 | } | ||
| 524 | } else if (argCount == 1) { | ||
| 525 | if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) { | ||
| 526 | pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null); | ||
| 527 | } else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) { | ||
| 528 | // Simple setter | ||
| 529 | pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method); | ||
| 530 | if (throwsException(method, PropertyVetoException.class)) { | ||
| 531 | pd.setConstrained(true); | ||
| 532 | } | ||
| 533 | } | ||
| 534 | } else if (argCount == 2) { | ||
| 535 | if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) { | ||
| 536 | pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method); | ||
| 537 | if (throwsException(method, PropertyVetoException.class)) { | ||
| 538 | pd.setConstrained(true); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | } | ||
| 542 | } catch (IntrospectionException ex) { | ||
| 543 | // This happens if a PropertyDescriptor or IndexedPropertyDescriptor | ||
| 544 | // constructor fins that the method violates details of the deisgn | ||
| 545 | // pattern, e.g. by having an empty name, or a getter returning | ||
| 546 | // void , or whatever. | ||
| 547 | pd = null; | ||
| 548 | } | ||
| 549 | |||
| 550 | if (pd != null) { | ||
| 551 | // If this class or one of its base classes is a PropertyChange | ||
| 552 | // source, then we assume that any properties we discover are "bound". | ||
| 553 | if (propertyChangeSource) { | ||
| 554 | pd.setBound(true); | ||
| 555 | } | ||
| 556 | addPropertyDescriptor(pd); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | } | ||
| 560 | processPropertyDescriptors(); | ||
| 561 | |||
| 562 | // Allocate and populate the result array. | ||
| 563 | PropertyDescriptor result[] = new PropertyDescriptor[properties.size()]; | ||
| 564 | result = (PropertyDescriptor[])properties.values().toArray(result); | ||
| 565 | |||
| 566 | // Set the default index. | ||
| 567 | if (defaultPropertyName != null) { | ||
| 568 | for (int i = 0; i < result.length; i++) { | ||
| 569 | if (defaultPropertyName.equals(result[i].getName())) { | ||
| 570 | defaultPropertyIndex = i; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | return result; | ||
| 576 | } | ||
| 577 | |||
| 578 | private HashMap pdStore = new HashMap(); | ||
| 579 | |||
| 580 | /** | ||
| 581 | * Adds the property descriptor to the list store. | ||
| 582 | */ | ||
| 583 | private void addPropertyDescriptor(PropertyDescriptor pd) { | ||
| 584 | String propName = pd.getName(); | ||
| 585 | List list = (List)pdStore.get(propName); | ||
| 586 | if (list == null) { | ||
| 587 | list = new ArrayList(); | ||
| 588 | pdStore.put(propName, list); | ||
| 589 | } | ||
| 590 | if (this.beanClass != pd.getClass0()) { | ||
| 591 | // replace existing property descriptor | ||
| 592 | // only if we have types to resolve | ||
| 593 | // in the context of this.beanClass | ||
| 594 | try { | ||
| 595 | String name = pd.getName(); | ||
| 596 | Method read = pd.getReadMethod(); | ||
| 597 | Method write = pd.getWriteMethod(); | ||
| 598 | boolean cls = true; | ||
| 599 | if (read != null) cls = cls && read.getGenericReturnType() instanceof Class; | ||
| 600 | if (write != null) cls = cls && write.getGenericParameterTypes()[0] instanceof Class; | ||
| 601 | if (pd instanceof IndexedPropertyDescriptor) { | ||
| 602 | IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)pd; | ||
| 603 | Method readI = ipd.getIndexedReadMethod(); | ||
| 604 | Method writeI = ipd.getIndexedWriteMethod(); | ||
| 605 | if (readI != null) cls = cls && readI.getGenericReturnType() instanceof Class; | ||
| 606 | if (writeI != null) cls = cls && writeI.getGenericParameterTypes()[1] instanceof Class; | ||
| 607 | if (!cls) { | ||
| 608 | pd = new IndexedPropertyDescriptor(this.beanClass, name, read, write, readI, writeI); | ||
| 609 | } | ||
| 610 | } else if (!cls) { | ||
| 611 | pd = new PropertyDescriptor(this.beanClass, name, read, write); | ||
| 612 | } | ||
| 613 | } catch ( IntrospectionException e ) { | ||
| 614 | } | ||
| 615 | } | ||
| 616 | list.add(pd); | ||
| 617 | } | ||
| 618 | |||
| 619 | private void addPropertyDescriptors(PropertyDescriptor[] descriptors) { | ||
| 620 | if (descriptors != null) { | ||
| 621 | for (PropertyDescriptor descriptor : descriptors) { | ||
| 622 | addPropertyDescriptor(descriptor); | ||
| 623 | } | ||
| 624 | } | ||
| 625 | } | ||
| 626 | |||
| 627 | private PropertyDescriptor[] getPropertyDescriptors(BeanInfo info) { | ||
| 628 | PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); | ||
| 629 | int index = info.getDefaultPropertyIndex(); | ||
| 630 | if ((0 <= index) && (index < descriptors.length)) { | ||
| 631 | this.defaultPropertyName = descriptors[index].getName(); | ||
| 632 | } | ||
| 633 | return descriptors; | ||
| 634 | } | ||
| 635 | |||
| 636 | /** | ||
| 637 | * Populates the property descriptor table by merging the | ||
| 638 | * lists of Property descriptors. | ||
| 639 | */ | ||
| 640 | private void processPropertyDescriptors() { | ||
| 641 | if (properties == null) { | ||
| 642 | properties = new TreeMap(); | ||
| 643 | } | ||
| 644 | |||
| 645 | List list; | ||
| 646 | |||
| 647 | PropertyDescriptor pd, gpd, spd; | ||
| 648 | IndexedPropertyDescriptor ipd, igpd, ispd; | ||
| 649 | |||
| 650 | Iterator it = pdStore.values().iterator(); | ||
| 651 | while (it.hasNext()) { | ||
| 652 | pd = null; gpd = null; spd = null; | ||
| 653 | ipd = null; igpd = null; ispd = null; | ||
| 654 | |||
| 655 | list = (List)it.next(); | ||
| 656 | |||
| 657 | // First pass. Find the latest getter method. Merge properties | ||
| 658 | // of previous getter methods. | ||
| 659 | for (int i = 0; i < list.size(); i++) { | ||
| 660 | pd = (PropertyDescriptor)list.get(i); | ||
| 661 | if (pd instanceof IndexedPropertyDescriptor) { | ||
| 662 | ipd = (IndexedPropertyDescriptor)pd; | ||
| 663 | if (ipd.getIndexedReadMethod() != null) { | ||
| 664 | if (igpd != null) { | ||
| 665 | igpd = new IndexedPropertyDescriptor(igpd, ipd); | ||
| 666 | } else { | ||
| 667 | igpd = ipd; | ||
| 668 | } | ||
| 669 | } | ||
| 670 | } else { | ||
| 671 | if (pd.getReadMethod() != null) { | ||
| 672 | if (gpd != null) { | ||
| 673 | // Don't replace the existing read | ||
| 674 | // method if it starts with "is" | ||
| 675 | Method method = gpd.getReadMethod(); | ||
| 676 | if (!method.getName().startsWith(IS_PREFIX)) { | ||
| 677 | gpd = new PropertyDescriptor(gpd, pd); | ||
| 678 | } | ||
| 679 | } else { | ||
| 680 | gpd = pd; | ||
| 681 | } | ||
| 682 | } | ||
| 683 | } | ||
| 684 | } | ||
| 685 | |||
| 686 | // Second pass. Find the latest setter method which | ||
| 687 | // has the same type as the getter method. | ||
| 688 | for (int i = 0; i < list.size(); i++) { | ||
| 689 | pd = (PropertyDescriptor)list.get(i); | ||
| 690 | if (pd instanceof IndexedPropertyDescriptor) { | ||
| 691 | ipd = (IndexedPropertyDescriptor)pd; | ||
| 692 | if (ipd.getIndexedWriteMethod() != null) { | ||
| 693 | if (igpd != null) { | ||
| 694 | if (igpd.getIndexedPropertyType() | ||
| 695 | == ipd.getIndexedPropertyType()) { | ||
| 696 | if (ispd != null) { | ||
| 697 | ispd = new IndexedPropertyDescriptor(ispd, ipd); | ||
| 698 | } else { | ||
| 699 | ispd = ipd; | ||
| 700 | } | ||
| 701 | } | ||
| 702 | } else { | ||
| 703 | if (ispd != null) { | ||
| 704 | ispd = new IndexedPropertyDescriptor(ispd, ipd); | ||
| 705 | } else { | ||
| 706 | ispd = ipd; | ||
| 707 | } | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } else { | ||
| 711 | if (pd.getWriteMethod() != null) { | ||
| 712 | if (gpd != null) { | ||
| 713 | if (gpd.getPropertyType() == pd.getPropertyType()) { | ||
| 714 | if (spd != null) { | ||
| 715 | spd = new PropertyDescriptor(spd, pd); | ||
| 716 | } else { | ||
| 717 | spd = pd; | ||
| 718 | } | ||
| 719 | } | ||
| 720 | } else { | ||
| 721 | if (spd != null) { | ||
| 722 | spd = new PropertyDescriptor(spd, pd); | ||
| 723 | } else { | ||
| 724 | spd = pd; | ||
| 725 | } | ||
| 726 | } | ||
| 727 | } | ||
| 728 | } | ||
| 729 | } | ||
| 730 | |||
| 731 | // At this stage we should have either PDs or IPDs for the | ||
| 732 | // representative getters and setters. The order at which the | ||
| 733 | // property descriptors are determined represent the | ||
| 734 | // precedence of the property ordering. | ||
| 735 | pd = null; ipd = null; | ||
| 736 | |||
| 737 | if (igpd != null && ispd != null) { | ||
| 738 | // Complete indexed properties set | ||
| 739 | // Merge any classic property descriptors | ||
| 740 | if (gpd != null) { | ||
| 741 | PropertyDescriptor tpd = mergePropertyDescriptor(igpd, gpd); | ||
| 742 | if (tpd instanceof IndexedPropertyDescriptor) { | ||
| 743 | igpd = (IndexedPropertyDescriptor)tpd; | ||
| 744 | } | ||
| 745 | } | ||
| 746 | if (spd != null) { | ||
| 747 | PropertyDescriptor tpd = mergePropertyDescriptor(ispd, spd); | ||
| 748 | if (tpd instanceof IndexedPropertyDescriptor) { | ||
| 749 | ispd = (IndexedPropertyDescriptor)tpd; | ||
| 750 | } | ||
| 751 | } | ||
| 752 | if (igpd == ispd) { | ||
| 753 | pd = igpd; | ||
| 754 | } else { | ||
| 755 | pd = mergePropertyDescriptor(igpd, ispd); | ||
| 756 | } | ||
| 757 | } else if (gpd != null && spd != null) { | ||
| 758 | // Complete simple properties set | ||
| 759 | if (gpd == spd) { | ||
| 760 | pd = gpd; | ||
| 761 | } else { | ||
| 762 | pd = mergePropertyDescriptor(gpd, spd); | ||
| 763 | } | ||
| 764 | } else if (ispd != null) { | ||
| 765 | // indexed setter | ||
| 766 | pd = ispd; | ||
| 767 | // Merge any classic property descriptors | ||
| 768 | if (spd != null) { | ||
| 769 | pd = mergePropertyDescriptor(ispd, spd); | ||
| 770 | } | ||
| 771 | if (gpd != null) { | ||
| 772 | pd = mergePropertyDescriptor(ispd, gpd); | ||
| 773 | } | ||
| 774 | } else if (igpd != null) { | ||
| 775 | // indexed getter | ||
| 776 | pd = igpd; | ||
| 777 | // Merge any classic property descriptors | ||
| 778 | if (gpd != null) { | ||
| 779 | pd = mergePropertyDescriptor(igpd, gpd); | ||
| 780 | } | ||
| 781 | if (spd != null) { | ||
| 782 | pd = mergePropertyDescriptor(igpd, spd); | ||
| 783 | } | ||
| 784 | } else if (spd != null) { | ||
| 785 | // simple setter | ||
| 786 | pd = spd; | ||
| 787 | } else if (gpd != null) { | ||
| 788 | // simple getter | ||
| 789 | pd = gpd; | ||
| 790 | } | ||
| 791 | |||
| 792 | // Very special case to ensure that an IndexedPropertyDescriptor | ||
| 793 | // doesn't contain less information than the enclosed | ||
| 794 | // PropertyDescriptor. If it does, then recreate as a | ||
| 795 | // PropertyDescriptor. See 4168833 | ||
| 796 | if (pd instanceof IndexedPropertyDescriptor) { | ||
| 797 | ipd = (IndexedPropertyDescriptor)pd; | ||
| 798 | if (ipd.getIndexedReadMethod() == null && ipd.getIndexedWriteMethod() == null) { | ||
| 799 | pd = new PropertyDescriptor(ipd); | ||
| 800 | } | ||
| 801 | } | ||
| 802 | |||
| 803 | // Find the first property descriptor | ||
| 804 | // which does not have getter and setter methods. | ||
| 805 | // See regression bug 4984912. | ||
| 806 | if ( (pd == null) && (list.size() > 0) ) { | ||
| 807 | pd = (PropertyDescriptor) list.get(0); | ||
| 808 | } | ||
| 809 | |||
| 810 | if (pd != null) { | ||
| 811 | properties.put(pd.getName(), pd); | ||
| 812 | } | ||
| 813 | } | ||
| 814 | } | ||
| 815 | |||
| 816 | /** | ||
| 817 | * Adds the property descriptor to the indexedproperty descriptor only if the | ||
| 818 | * types are the same. | ||
| 819 | * | ||
| 820 | * The most specific property descriptor will take precedence. | ||
| 821 | */ | ||
| 822 | private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd, | ||
| 823 | PropertyDescriptor pd) { | ||
| 824 | PropertyDescriptor result = null; | ||
| 825 | |||
| 826 | Class propType = pd.getPropertyType(); | ||
| 827 | Class ipropType = ipd.getIndexedPropertyType(); | ||
| 828 | |||
| 829 | if (propType.isArray() && propType.getComponentType() == ipropType) { | ||
| 830 | if (pd.getClass0().isAssignableFrom(ipd.getClass0())) { | ||
| 831 | result = new IndexedPropertyDescriptor(pd, ipd); | ||
| 832 | } else { | ||
| 833 | result = new IndexedPropertyDescriptor(ipd, pd); | ||
| 834 | } | ||
| 835 | } else { | ||
| 836 | // Cannot merge the pd because of type mismatch | ||
| 837 | // Return the most specific pd | ||
| 838 | if (pd.getClass0().isAssignableFrom(ipd.getClass0())) { | ||
| 839 | result = ipd; | ||
| 840 | } else { | ||
| 841 | result = pd; | ||
| 842 | // Try to add methods which may have been lost in the type change | ||
| 843 | // See 4168833 | ||
| 844 | Method write = result.getWriteMethod(); | ||
| 845 | Method read = result.getReadMethod(); | ||
| 846 | |||
| 847 | if (read == null && write != null) { | ||
| 848 | read = findMethod(result.getClass0(), | ||
| 849 | GET_PREFIX + NameGenerator.capitalize(result.getName()), 0); | ||
| 850 | if (read != null) { | ||
| 851 | try { | ||
| 852 | result.setReadMethod(read); | ||
| 853 | } catch (IntrospectionException ex) { | ||
| 854 | // no consequences for failure. | ||
| 855 | } | ||
| 856 | } | ||
| 857 | } | ||
| 858 | if (write == null && read != null) { | ||
| 859 | write = findMethod(result.getClass0(), | ||
| 860 | SET_PREFIX + NameGenerator.capitalize(result.getName()), 1, | ||
| 861 | new Class[] { FeatureDescriptor.getReturnType(result.getClass0(), read) }); | ||
| 862 | if (write != null) { | ||
| 863 | try { | ||
| 864 | result.setWriteMethod(write); | ||
| 865 | } catch (IntrospectionException ex) { | ||
| 866 | // no consequences for failure. | ||
| 867 | } | ||
| 868 | } | ||
| 869 | } | ||
| 870 | } | ||
| 871 | } | ||
| 872 | return result; | ||
| 873 | } | ||
| 874 | |||
| 875 | // Handle regular pd merge | ||
| 876 | private PropertyDescriptor mergePropertyDescriptor(PropertyDescriptor pd1, | ||
| 877 | PropertyDescriptor pd2) { | ||
| 878 | if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) { | ||
| 879 | return new PropertyDescriptor(pd1, pd2); | ||
| 880 | } else { | ||
| 881 | return new PropertyDescriptor(pd2, pd1); | ||
| 882 | } | ||
| 883 | } | ||
| 884 | |||
| 885 | // Handle regular ipd merge | ||
| 886 | private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd1, | ||
| 887 | IndexedPropertyDescriptor ipd2) { | ||
| 888 | if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) { | ||
| 889 | return new IndexedPropertyDescriptor(ipd1, ipd2); | ||
| 890 | } else { | ||
| 891 | return new IndexedPropertyDescriptor(ipd2, ipd1); | ||
| 892 | } | ||
| 893 | } | ||
| 894 | |||
| 895 | /** | ||
| 896 | * @return An array of EventSetDescriptors describing the kinds of | ||
| 897 | * events fired by the target bean. | ||
| 898 | */ | ||
| 899 | private EventSetDescriptor[] getTargetEventInfo() throws IntrospectionException { | ||
| 900 | if (events == null) { | ||
| 901 | events = new HashMap(); | ||
| 902 | } | ||
| 903 | |||
| 904 | // Check if the bean has its own BeanInfo that will provide | ||
| 905 | // explicit information. | ||
| 906 | EventSetDescriptor[] explicitEvents = null; | ||
| 907 | if (explicitBeanInfo != null) { | ||
| 908 | explicitEvents = explicitBeanInfo.getEventSetDescriptors(); | ||
| 909 | int ix = explicitBeanInfo.getDefaultEventIndex(); | ||
| 910 | if (ix >= 0 && ix < explicitEvents.length) { | ||
| 911 | defaultEventName = explicitEvents[ix].getName(); | ||
| 912 | } | ||
| 913 | } | ||
| 914 | |||
| 915 | if (explicitEvents == null && superBeanInfo != null) { | ||
| 916 | // We have no explicit BeanInfo events. Check with our parent. | ||
| 917 | EventSetDescriptor supers[] = superBeanInfo.getEventSetDescriptors(); | ||
| 918 | for (int i = 0 ; i < supers.length; i++) { | ||
| 919 | addEvent(supers[i]); | ||
| 920 | } | ||
| 921 | int ix = superBeanInfo.getDefaultEventIndex(); | ||
| 922 | if (ix >= 0 && ix < supers.length) { | ||
| 923 | defaultEventName = supers[ix].getName(); | ||
| 924 | } | ||
| 925 | } | ||
| 926 | |||
| 927 | for (int i = 0; i < additionalBeanInfo.length; i++) { | ||
| 928 | EventSetDescriptor additional[] = additionalBeanInfo[i].getEventSetDescriptors(); | ||
| 929 | if (additional != null) { | ||
| 930 | for (int j = 0 ; j < additional.length; j++) { | ||
| 931 | addEvent(additional[j]); | ||
| 932 | } | ||
| 933 | } | ||
| 934 | } | ||
| 935 | |||
| 936 | if (explicitEvents != null) { | ||
| 937 | // Add the explicit explicitBeanInfo data to our results. | ||
| 938 | for (int i = 0 ; i < explicitEvents.length; i++) { | ||
| 939 | addEvent(explicitEvents[i]); | ||
| 940 | } | ||
| 941 | |||
| 942 | } else { | ||
| 943 | |||
| 944 | // Apply some reflection to the current class. | ||
| 945 | |||
| 946 | // Get an array of all the public beans methods at this level | ||
| 947 | Method methodList[] = getPublicDeclaredMethods(beanClass); | ||
| 948 | |||
| 949 | // Find all suitable "add", "remove" and "get" Listener methods | ||
| 950 | // The name of the listener type is the key for these hashtables | ||
| 951 | // i.e, ActionListener | ||
| 952 | Map adds = null; | ||
| 953 | Map removes = null; | ||
| 954 | Map gets = null; | ||
| 955 | |||
| 956 | for (int i = 0; i < methodList.length; i++) { | ||
| 957 | Method method = methodList[i]; | ||
| 958 | if (method == null) { | ||
| 959 | continue; | ||
| 960 | } | ||
| 961 | // skip static methods. | ||
| 962 | int mods = method.getModifiers(); | ||
| 963 | if (Modifier.isStatic(mods)) { | ||
| 964 | continue; | ||
| 965 | } | ||
| 966 | String name = method.getName(); | ||
| 967 | // Optimization avoid getParameterTypes | ||
| 968 | if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX) | ||
| 969 | && !name.startsWith(GET_PREFIX)) { | ||
| 970 | continue; | ||
| 971 | } | ||
| 972 | |||
| 973 | Class argTypes[] = FeatureDescriptor.getParameterTypes(beanClass, method); | ||
| 974 | Class resultType = FeatureDescriptor.getReturnType(beanClass, method); | ||
| 975 | |||
| 976 | if (name.startsWith(ADD_PREFIX) && argTypes.length == 1 && | ||
| 977 | resultType == Void.TYPE && | ||
| 978 | Introspector.isSubclass(argTypes[0], eventListenerType)) { | ||
| 979 | String listenerName = name.substring(3); | ||
| 980 | if (listenerName.length() > 0 && | ||
| 981 | argTypes[0].getName().endsWith(listenerName)) { | ||
| 982 | if (adds == null) { | ||
| 983 | adds = new HashMap(); | ||
| 984 | } | ||
| 985 | adds.put(listenerName, method); | ||
| 986 | } | ||
| 987 | } | ||
| 988 | else if (name.startsWith(REMOVE_PREFIX) && argTypes.length == 1 && | ||
| 989 | resultType == Void.TYPE && | ||
| 990 | Introspector.isSubclass(argTypes[0], eventListenerType)) { | ||
| 991 | String listenerName = name.substring(6); | ||
| 992 | if (listenerName.length() > 0 && | ||
| 993 | argTypes[0].getName().endsWith(listenerName)) { | ||
| 994 | if (removes == null) { | ||
| 995 | removes = new HashMap(); | ||
| 996 | } | ||
| 997 | removes.put(listenerName, method); | ||
| 998 | } | ||
| 999 | } | ||
| 1000 | else if (name.startsWith(GET_PREFIX) && argTypes.length == 0 && | ||
| 1001 | resultType.isArray() && | ||
| 1002 | Introspector.isSubclass(resultType.getComponentType(), | ||
| 1003 | eventListenerType)) { | ||
| 1004 | String listenerName = name.substring(3, name.length() - 1); | ||
| 1005 | if (listenerName.length() > 0 && | ||
| 1006 | resultType.getComponentType().getName().endsWith(listenerName)) { | ||
| 1007 | if (gets == null) { | ||
| 1008 | gets = new HashMap(); | ||
| 1009 | } | ||
| 1010 | gets.put(listenerName, method); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | if (adds != null && removes != null) { | ||
| 1016 | // Now look for matching addFooListener+removeFooListener pairs. | ||
| 1017 | // Bonus if there is a matching getFooListeners method as well. | ||
| 1018 | Iterator keys = adds.keySet().iterator(); | ||
| 1019 | while (keys.hasNext()) { | ||
| 1020 | String listenerName = (String) keys.next(); | ||
| 1021 | // Skip any "add" which doesn't have a matching "remove" or | ||
| 1022 | // a listener name that doesn't end with Listener | ||
| 1023 | if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) { | ||
| 1024 | continue; | ||
| 1025 | } | ||
| 1026 | String eventName = decapitalize(listenerName.substring(0, listenerName.length()-8)); | ||
| 1027 | Method addMethod = (Method)adds.get(listenerName); | ||
| 1028 | Method removeMethod = (Method)removes.get(listenerName); | ||
| 1029 | Method getMethod = null; | ||
| 1030 | if (gets != null) { | ||
| 1031 | getMethod = (Method)gets.get(listenerName); | ||
| 1032 | } | ||
| 1033 | Class argType = FeatureDescriptor.getParameterTypes(beanClass, addMethod)[0]; | ||
| 1034 | |||
| 1035 | // generate a list of Method objects for each of the target methods: | ||
| 1036 | Method allMethods[] = getPublicDeclaredMethods(argType); | ||
| 1037 | List validMethods = new ArrayList(allMethods.length); | ||
| 1038 | for (int i = 0; i < allMethods.length; i++) { | ||
| 1039 | if (allMethods[i] == null) { | ||
| 1040 | continue; | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | if (isEventHandler(allMethods[i])) { | ||
| 1044 | validMethods.add(allMethods[i]); | ||
| 1045 | } | ||
| 1046 | } | ||
| 1047 | Method[] methods = (Method[])validMethods.toArray(new Method[validMethods.size()]); | ||
| 1048 | |||
| 1049 | EventSetDescriptor esd = new EventSetDescriptor(eventName, argType, | ||
| 1050 | methods, addMethod, | ||
| 1051 | removeMethod, | ||
| 1052 | getMethod); | ||
| 1053 | |||
| 1054 | // If the adder method throws the TooManyListenersException then it | ||
| 1055 | // is a Unicast event source. | ||
| 1056 | if (throwsException(addMethod, | ||
| 1057 | java.util.TooManyListenersException.class)) { | ||
| 1058 | esd.setUnicast(true); | ||
| 1059 | } | ||
| 1060 | addEvent(esd); | ||
| 1061 | } | ||
| 1062 | } // if (adds != null ... | ||
| 1063 | } | ||
| 1064 | EventSetDescriptor[] result; | ||
| 1065 | if (events.size() == 0) { | ||
| 1066 | result = EMPTY_EVENTSETDESCRIPTORS; | ||
| 1067 | } else { | ||
| 1068 | // Allocate and populate the result array. | ||
| 1069 | result = new EventSetDescriptor[events.size()]; | ||
| 1070 | result = (EventSetDescriptor[])events.values().toArray(result); | ||
| 1071 | |||
| 1072 | // Set the default index. | ||
| 1073 | if (defaultEventName != null) { | ||
| 1074 | for (int i = 0; i < result.length; i++) { | ||
| 1075 | if (defaultEventName.equals(result[i].getName())) { | ||
| 1076 | defaultEventIndex = i; | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | } | ||
| 1080 | } | ||
| 1081 | return result; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | private void addEvent(EventSetDescriptor esd) { | ||
| 1085 | String key = esd.getName(); | ||
| 1086 | if (esd.getName().equals("propertyChange")) { | ||
| 1087 | propertyChangeSource = true; | ||
| 1088 | } | ||
| 1089 | EventSetDescriptor old = (EventSetDescriptor)events.get(key); | ||
| 1090 | if (old == null) { | ||
| 1091 | events.put(key, esd); | ||
| 1092 | return; | ||
| 1093 | } | ||
| 1094 | EventSetDescriptor composite = new EventSetDescriptor(old, esd); | ||
| 1095 | events.put(key, composite); | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | /** | ||
| 1099 | * @return An array of MethodDescriptors describing the private | ||
| 1100 | * methods supported by the target bean. | ||
| 1101 | */ | ||
| 1102 | private MethodDescriptor[] getTargetMethodInfo() { | ||
| 1103 | if (methods == null) { | ||
| 1104 | methods = new HashMap(100); | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | // Check if the bean has its own BeanInfo that will provide | ||
| 1108 | // explicit information. | ||
| 1109 | MethodDescriptor[] explicitMethods = null; | ||
| 1110 | if (explicitBeanInfo != null) { | ||
| 1111 | explicitMethods = explicitBeanInfo.getMethodDescriptors(); | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | if (explicitMethods == null && superBeanInfo != null) { | ||
| 1115 | // We have no explicit BeanInfo methods. Check with our parent. | ||
| 1116 | MethodDescriptor supers[] = superBeanInfo.getMethodDescriptors(); | ||
| 1117 | for (int i = 0 ; i < supers.length; i++) { | ||
| 1118 | addMethod(supers[i]); | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | for (int i = 0; i < additionalBeanInfo.length; i++) { | ||
| 1123 | MethodDescriptor additional[] = additionalBeanInfo[i].getMethodDescriptors(); | ||
| 1124 | if (additional != null) { | ||
| 1125 | for (int j = 0 ; j < additional.length; j++) { | ||
| 1126 | addMethod(additional[j]); | ||
| 1127 | } | ||
| 1128 | } | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | if (explicitMethods != null) { | ||
| 1132 | // Add the explicit explicitBeanInfo data to our results. | ||
| 1133 | for (int i = 0 ; i < explicitMethods.length; i++) { | ||
| 1134 | addMethod(explicitMethods[i]); | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | } else { | ||
| 1138 | |||
| 1139 | // Apply some reflection to the current class. | ||
| 1140 | |||
| 1141 | // First get an array of all the beans methods at this level | ||
| 1142 | Method methodList[] = getPublicDeclaredMethods(beanClass); | ||
| 1143 | |||
| 1144 | // Now analyze each method. | ||
| 1145 | for (int i = 0; i < methodList.length; i++) { | ||
| 1146 | Method method = methodList[i]; | ||
| 1147 | if (method == null) { | ||
| 1148 | continue; | ||
| 1149 | } | ||
| 1150 | MethodDescriptor md = new MethodDescriptor(method); | ||
| 1151 | addMethod(md); | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | // Allocate and populate the result array. | ||
| 1156 | MethodDescriptor result[] = new MethodDescriptor[methods.size()]; | ||
| 1157 | result = (MethodDescriptor[])methods.values().toArray(result); | ||
| 1158 | |||
| 1159 | return result; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | private void addMethod(MethodDescriptor md) { | ||
| 1163 | // We have to be careful here to distinguish method by both name | ||
| 1164 | // and argument lists. | ||
| 1165 | // This method gets called a *lot, so we try to be efficient. | ||
| 1166 | String name = md.getName(); | ||
| 1167 | |||
| 1168 | MethodDescriptor old = (MethodDescriptor)methods.get(name); | ||
| 1169 | if (old == null) { | ||
| 1170 | // This is the common case. | ||
| 1171 | methods.put(name, md); | ||
| 1172 | return; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | // We have a collision on method names. This is rare. | ||
| 1176 | |||
| 1177 | // Check if old and md have the same type. | ||
| 1178 | String[] p1 = md.getParamNames(); | ||
| 1179 | String[] p2 = old.getParamNames(); | ||
| 1180 | |||
| 1181 | boolean match = false; | ||
| 1182 | if (p1.length == p2.length) { | ||
| 1183 | match = true; | ||
| 1184 | for (int i = 0; i < p1.length; i++) { | ||
| 1185 | if (p1[i] != p2[i]) { | ||
| 1186 | match = false; | ||
| 1187 | break; | ||
| 1188 | } | ||
| 1189 | } | ||
| 1190 | } | ||
| 1191 | if (match) { | ||
| 1192 | MethodDescriptor composite = new MethodDescriptor(old, md); | ||
| 1193 | methods.put(name, composite); | ||
| 1194 | return; | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | // We have a collision on method names with different type signatures. | ||
| 1198 | // This is very rare. | ||
| 1199 | |||
| 1200 | String longKey = makeQualifiedMethodName(name, p1); | ||
| 1201 | old = (MethodDescriptor)methods.get(longKey); | ||
| 1202 | if (old == null) { | ||
| 1203 | methods.put(longKey, md); | ||
| 1204 | return; | ||
| 1205 | } | ||
| 1206 | MethodDescriptor composite = new MethodDescriptor(old, md); | ||
| 1207 | methods.put(longKey, composite); | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | /** | ||
| 1211 | * Creates a key for a method in a method cache. | ||
| 1212 | */ | ||
| 1213 | private static String makeQualifiedMethodName(String name, String[] params) { | ||
| 1214 | StringBuffer sb = new StringBuffer(name); | ||
| 1215 | sb.append('='); | ||
| 1216 | for (int i = 0; i < params.length; i++) { | ||
| 1217 | sb.append(':'); | ||
| 1218 | sb.append(params[i]); | ||
| 1219 | } | ||
| 1220 | return sb.toString(); | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | private int getTargetDefaultEventIndex() { | ||
| 1224 | return defaultEventIndex; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | private int getTargetDefaultPropertyIndex() { | ||
| 1228 | return defaultPropertyIndex; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | private BeanDescriptor getTargetBeanDescriptor() { | ||
| 1232 | // Use explicit info, if available, | ||
| 1233 | if (explicitBeanInfo != null) { | ||
| 1234 | BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor(); | ||
| 1235 | if (bd != null) { | ||
| 1236 | return (bd); | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | // OK, fabricate a default BeanDescriptor. | ||
| 1240 | return new BeanDescriptor(this.beanClass, findCustomizerClass(this.beanClass)); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | private static Class<?> findCustomizerClass(Class<?> type) { | ||
| 1244 | String name = type.getName() + "Customizer"; | ||
| 1245 | try { | ||
| 1246 | type = ClassFinder.findClass(name, type.getClassLoader()); | ||
| 1247 | // Each customizer should inherit java.awt.Component and implement java.beans.Customizer | ||
| 1248 | // according to the section 9.3 of JavaBeans™ specification | ||
| 1249 | if (Component.class.isAssignableFrom(type) && Customizer.class.isAssignableFrom(type)) { | ||
| 1250 | return type; | ||
| 1251 | } | ||
| 1252 | } | ||
| 1253 | catch (Exception exception) { | ||
| 1254 | // ignore any exceptions | ||
| 1255 | } | ||
| 1256 | return null; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | private boolean isEventHandler(Method m) { | ||
| 1260 | // We assume that a method is an event handler if it has a single | ||
| 1261 | // argument, whose type inherit from java.util.Event. | ||
| 1262 | Class argTypes[] = FeatureDescriptor.getParameterTypes(beanClass, m); | ||
| 1263 | if (argTypes.length != 1) { | ||
| 1264 | return false; | ||
| 1265 | } | ||
| 1266 | return isSubclass(argTypes[0], EventObject.class); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | /* | ||
| 1270 | * Internal method to return *public* methods within a class. | ||
| 1271 | */ | ||
| 1272 | private static Method[] getPublicDeclaredMethods(Class clz) { | ||
| 1273 | // Looking up Class.getDeclaredMethods is relatively expensive, | ||
| 1274 | // so we cache the results. | ||
| 1275 | if (!ReflectUtil.isPackageAccessible(clz)) { | ||
| 1276 | return new Method[0]; | ||
| 1277 | } | ||
| 1278 | synchronized (BEANINFO_CACHE) { | ||
| 1279 | Method[] result = declaredMethodCache.get(clz); | ||
| 1280 | if (result == null) { | ||
| 1281 | result = clz.getMethods(); | ||
| 1282 | for (int i = 0; i < result.length; i++) { | ||
| 1283 | Method method = result[i]; | ||
| 1284 | if (!method.getDeclaringClass().equals(clz)) { | ||
| 1285 | result[i] = null; | ||
| 1286 | } | ||
| 1287 | } | ||
| 1288 | declaredMethodCache.put(clz, result); | ||
| 1289 | } | ||
| 1290 | return result; | ||
| 1291 | } | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | //====================================================================== | ||
| 1295 | // Package private support methods. | ||
| 1296 | //====================================================================== | ||
| 1297 | |||
| 1298 | /** | ||
| 1299 | * Internal support for finding a target methodName with a given | ||
| 1300 | * parameter list on a given class. | ||
| 1301 | */ | ||
| 1302 | private static Method internalFindMethod(Class start, String methodName, | ||
| 1303 | int argCount, Class args[]) { | ||
| 1304 | // For overriden methods we need to find the most derived version. | ||
| 1305 | // So we start with the given class and walk up the superclass chain. | ||
| 1306 | |||
| 1307 | Method method = null; | ||
| 1308 | |||
| 1309 | for (Class cl = start; cl != null; cl = cl.getSuperclass()) { | ||
| 1310 | Method methods[] = getPublicDeclaredMethods(cl); | ||
| 1311 | for (int i = 0; i < methods.length; i++) { | ||
| 1312 | method = methods[i]; | ||
| 1313 | if (method == null) { | ||
| 1314 | continue; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | // make sure method signature matches. | ||
| 1318 | Class params[] = FeatureDescriptor.getParameterTypes(start, method); | ||
| 1319 | if (method.getName().equals(methodName) && | ||
| 1320 | params.length == argCount) { | ||
| 1321 | if (args != null) { | ||
| 1322 | boolean different = false; | ||
| 1323 | if (argCount > 0) { | ||
| 1324 | for (int j = 0; j < argCount; j++) { | ||
| 1325 | if (params[j] != args[j]) { | ||
| 1326 | different = true; | ||
| 1327 | continue; | ||
| 1328 | } | ||
| 1329 | } | ||
| 1330 | if (different) { | ||
| 1331 | continue; | ||
| 1332 | } | ||
| 1333 | } | ||
| 1334 | } | ||
| 1335 | return method; | ||
| 1336 | } | ||
| 1337 | } | ||
| 1338 | } | ||
| 1339 | method = null; | ||
| 1340 | |||
| 1341 | // Now check any inherited interfaces. This is necessary both when | ||
| 1342 | // the argument class is itself an interface, and when the argument | ||
| 1343 | // class is an abstract class. | ||
| 1344 | Class ifcs[] = start.getInterfaces(); | ||
| 1345 | for (int i = 0 ; i < ifcs.length; i++) { | ||
| 1346 | // Note: The original implementation had both methods calling | ||
| 1347 | // the 3 arg method. This is preserved but perhaps it should | ||
| 1348 | // pass the args array instead of null. | ||
| 1349 | method = internalFindMethod(ifcs[i], methodName, argCount, null); | ||
| 1350 | if (method != null) { | ||
| 1351 | break; | ||
| 1352 | } | ||
| 1353 | } | ||
| 1354 | return method; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | /** | ||
| 1358 | * Find a target methodName on a given class. | ||
| 1359 | */ | ||
| 1360 | static Method findMethod(Class cls, String methodName, int argCount) { | ||
| 1361 | return findMethod(cls, methodName, argCount, null); | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | /** | ||
| 1365 | * Find a target methodName with specific parameter list on a given class. | ||
| 1366 | * <p> | ||
| 1367 | * Used in the contructors of the EventSetDescriptor, | ||
| 1368 | * PropertyDescriptor and the IndexedPropertyDescriptor. | ||
| 1369 | * <p> | ||
| 1370 | * @param cls The Class object on which to retrieve the method. | ||
| 1371 | * @param methodName Name of the method. | ||
| 1372 | * @param argCount Number of arguments for the desired method. | ||
| 1373 | * @param args Array of argument types for the method. | ||
| 1374 | * @return the method or null if not found | ||
| 1375 | */ | ||
| 1376 | static Method findMethod(Class cls, String methodName, int argCount, | ||
| 1377 | Class args[]) { | ||
| 1378 | if (methodName == null) { | ||
| 1379 | return null; | ||
| 1380 | } | ||
| 1381 | return internalFindMethod(cls, methodName, argCount, args); | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | /** | ||
| 1385 | * Return true if class a is either equivalent to class b, or | ||
| 1386 | * if class a is a subclass of class b, i.e. if a either "extends" | ||
| 1387 | * or "implements" b. | ||
| 1388 | * Note tht either or both "Class" objects may represent interfaces. | ||
| 1389 | */ | ||
| 1390 | static boolean isSubclass(Class a, Class b) { | ||
| 1391 | // We rely on the fact that for any given java class or | ||
| 1392 | // primtitive type there is a unqiue Class object, so | ||
| 1393 | // we can use object equivalence in the comparisons. | ||
| 1394 | if (a == b) { | ||
| 1395 | return true; | ||
| 1396 | } | ||
| 1397 | if (a == null || b == null) { | ||
| 1398 | return false; | ||
| 1399 | } | ||
| 1400 | for (Class x = a; x != null; x = x.getSuperclass()) { | ||
| 1401 | if (x == b) { | ||
| 1402 | return true; | ||
| 1403 | } | ||
| 1404 | if (b.isInterface()) { | ||
| 1405 | Class interfaces[] = x.getInterfaces(); | ||
| 1406 | for (int i = 0; i < interfaces.length; i++) { | ||
| 1407 | if (isSubclass(interfaces[i], b)) { | ||
| 1408 | return true; | ||
| 1409 | } | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | } | ||
| 1413 | return false; | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | /** | ||
| 1417 | * Return true iff the given method throws the given exception. | ||
| 1418 | */ | ||
| 1419 | private boolean throwsException(Method method, Class exception) { | ||
| 1420 | Class exs[] = method.getExceptionTypes(); | ||
| 1421 | for (int i = 0; i < exs.length; i++) { | ||
| 1422 | if (exs[i] == exception) { | ||
| 1423 | return true; | ||
| 1424 | } | ||
| 1425 | } | ||
| 1426 | return false; | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | private static BeanInfoFinder getFinder() { | ||
| 1430 | AppContext context = AppContext.getAppContext(); | ||
| 1431 | Object object = context.get(FINDER_KEY); | ||
| 1432 | if (object instanceof BeanInfoFinder) { | ||
| 1433 | return (BeanInfoFinder) object; | ||
| 1434 | } | ||
| 1435 | BeanInfoFinder finder = new BeanInfoFinder(); | ||
| 1436 | context.put(FINDER_KEY, finder); | ||
| 1437 | return finder; | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | /** | ||
| 1441 | * Try to create an instance of a named class. | ||
| 1442 | * First try the classloader of "sibling", then try the system | ||
| 1443 | * classloader then the class loader of the current Thread. | ||
| 1444 | */ | ||
| 1445 | static Object instantiate(Class sibling, String className) | ||
| 1446 | throws InstantiationException, IllegalAccessException, | ||
| 1447 | ClassNotFoundException { | ||
| 1448 | // First check with sibling's classloader (if any). | ||
| 1449 | ClassLoader cl = sibling.getClassLoader(); | ||
| 1450 | Class cls = ClassFinder.findClass(className, cl); | ||
| 1451 | return cls.newInstance(); | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | } // end class Introspector | ||
| 1455 | |||
| 1456 | //=========================================================================== | ||
| 1457 | |||
| 1458 | /** | ||
| 1459 | * Package private implementation support class for Introspector's | ||
| 1460 | * internal use. | ||
| 1461 | * <p> | ||
| 1462 | * Mostly this is used as a placeholder for the descriptors. | ||
| 1463 | */ | ||
| 1464 | |||
| 1465 | class GenericBeanInfo extends SimpleBeanInfo { | ||
| 1466 | |||
| 1467 | private BeanDescriptor beanDescriptor; | ||
| 1468 | private EventSetDescriptor[] events; | ||
| 1469 | private int defaultEvent; | ||
| 1470 | private PropertyDescriptor[] properties; | ||
| 1471 | private int defaultProperty; | ||
| 1472 | private MethodDescriptor[] methods; | ||
| 1473 | private final Reference<BeanInfo> targetBeanInfoRef; | ||
| 1474 | |||
| 1475 | public GenericBeanInfo(BeanDescriptor beanDescriptor, | ||
| 1476 | EventSetDescriptor[] events, int defaultEvent, | ||
| 1477 | PropertyDescriptor[] properties, int defaultProperty, | ||
| 1478 | MethodDescriptor[] methods, BeanInfo targetBeanInfo) { | ||
| 1479 | this.beanDescriptor = beanDescriptor; | ||
| 1480 | this.events = events; | ||
| 1481 | this.defaultEvent = defaultEvent; | ||
| 1482 | this.properties = properties; | ||
| 1483 | this.defaultProperty = defaultProperty; | ||
| 1484 | this.methods = methods; | ||
| 1485 | this.targetBeanInfoRef = new SoftReference<BeanInfo>(targetBeanInfo); | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | /** | ||
| 1489 | * Package-private dup constructor | ||
| 1490 | * This must isolate the new object from any changes to the old object. | ||
| 1491 | */ | ||
| 1492 | GenericBeanInfo(GenericBeanInfo old) { | ||
| 1493 | |||
| 1494 | beanDescriptor = new BeanDescriptor(old.beanDescriptor); | ||
| 1495 | if (old.events != null) { | ||
| 1496 | int len = old.events.length; | ||
| 1497 | events = new EventSetDescriptor[len]; | ||
| 1498 | for (int i = 0; i < len; i++) { | ||
| 1499 | events[i] = new EventSetDescriptor(old.events[i]); | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | defaultEvent = old.defaultEvent; | ||
| 1503 | if (old.properties != null) { | ||
| 1504 | int len = old.properties.length; | ||
| 1505 | properties = new PropertyDescriptor[len]; | ||
| 1506 | for (int i = 0; i < len; i++) { | ||
| 1507 | PropertyDescriptor oldp = old.properties[i]; | ||
| 1508 | if (oldp instanceof IndexedPropertyDescriptor) { | ||
| 1509 | properties[i] = new IndexedPropertyDescriptor( | ||
| 1510 | (IndexedPropertyDescriptor) oldp); | ||
| 1511 | } else { | ||
| 1512 | properties[i] = new PropertyDescriptor(oldp); | ||
| 1513 | } | ||
| 1514 | } | ||
| 1515 | } | ||
| 1516 | defaultProperty = old.defaultProperty; | ||
| 1517 | if (old.methods != null) { | ||
| 1518 | int len = old.methods.length; | ||
| 1519 | methods = new MethodDescriptor[len]; | ||
| 1520 | for (int i = 0; i < len; i++) { | ||
| 1521 | methods[i] = new MethodDescriptor(old.methods[i]); | ||
| 1522 | } | ||
| 1523 | } | ||
| 1524 | this.targetBeanInfoRef = old.targetBeanInfoRef; | ||
| 1525 | } | ||
| 1526 | |||
| 1527 | public PropertyDescriptor[] getPropertyDescriptors() { | ||
| 1528 | return properties; | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | public int getDefaultPropertyIndex() { | ||
| 1532 | return defaultProperty; | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | public EventSetDescriptor[] getEventSetDescriptors() { | ||
| 1536 | return events; | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | public int getDefaultEventIndex() { | ||
| 1540 | return defaultEvent; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | public MethodDescriptor[] getMethodDescriptors() { | ||
| 1544 | return methods; | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | public BeanDescriptor getBeanDescriptor() { | ||
| 1548 | return beanDescriptor; | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | public java.awt.Image getIcon(int iconKind) { | ||
| 1552 | BeanInfo targetBeanInfo = this.targetBeanInfoRef.get(); | ||
| 1553 | if (targetBeanInfo != null) { | ||
| 1554 | return targetBeanInfo.getIcon(iconKind); | ||
| 1555 | } | ||
| 1556 | return super.getIcon(iconKind); | ||
| 1557 | } | ||
| 1558 | } | ||
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,1443 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import com.sun.beans.finder.PrimitiveWrapperMap; | ||
| 28 | |||
| 29 | import java.awt.AWTKeyStroke; | ||
| 30 | import java.awt.BorderLayout; | ||
| 31 | import java.awt.Dimension; | ||
| 32 | import java.awt.Color; | ||
| 33 | import java.awt.Font; | ||
| 34 | import java.awt.GridBagConstraints; | ||
| 35 | import java.awt.Insets; | ||
| 36 | import java.awt.Point; | ||
| 37 | import java.awt.Rectangle; | ||
| 38 | import java.awt.event.KeyEvent; | ||
| 39 | import java.awt.font.TextAttribute; | ||
| 40 | |||
| 41 | import java.lang.reflect.Array; | ||
| 42 | import java.lang.reflect.Constructor; | ||
| 43 | import java.lang.reflect.Field; | ||
| 44 | import java.lang.reflect.Method; | ||
| 45 | import java.lang.reflect.InvocationTargetException; | ||
| 46 | |||
| 47 | import java.security.AccessController; | ||
| 48 | import java.security.PrivilegedAction; | ||
| 49 | |||
| 50 | import java.util.*; | ||
| 51 | |||
| 52 | import javax.swing.Box; | ||
| 53 | import javax.swing.JLayeredPane; | ||
| 54 | import javax.swing.border.MatteBorder; | ||
| 55 | import javax.swing.plaf.ColorUIResource; | ||
| 56 | |||
| 57 | import sun.swing.PrintColorUIResource; | ||
| 58 | |||
| 59 | import java.util.Objects; | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Like the <code>Intropector</code>, the <code>MetaData</code> class | ||
| 63 | * contains <em>meta</em> objects that describe the way | ||
| 64 | * classes should express their state in terms of their | ||
| 65 | * own public APIs. | ||
| 66 | * | ||
| 67 | * @see java.beans.Intropector | ||
| 68 | * | ||
| 69 | * @author Philip Milne | ||
| 70 | * @author Steve Langley | ||
| 71 | */ | ||
| 72 | |||
| 73 | class NullPersistenceDelegate extends PersistenceDelegate { | ||
| 74 | // Note this will be called by all classes when they reach the | ||
| 75 | // top of their superclass chain. | ||
| 76 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 77 | } | ||
| 78 | protected Expression instantiate(Object oldInstance, Encoder out) { return null; } | ||
| 79 | |||
| 80 | public void writeObject(Object oldInstance, Encoder out) { | ||
| 81 | // System.out.println("NullPersistenceDelegate:writeObject " + oldInstance); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /** | ||
| 86 | * The persistence delegate for <CODE>enum</CODE> classes. | ||
| 87 | * | ||
| 88 | * @author Sergey A. Malenkov | ||
| 89 | */ | ||
| 90 | class EnumPersistenceDelegate extends PersistenceDelegate { | ||
| 91 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 92 | return oldInstance == newInstance; | ||
| 93 | } | ||
| 94 | |||
| 95 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 96 | Enum e = (Enum) oldInstance; | ||
| 97 | return new Expression(e, Enum.class, "valueOf", new Object[]{e.getDeclaringClass(), e.name()}); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | class PrimitivePersistenceDelegate extends PersistenceDelegate { | ||
| 102 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 103 | return oldInstance.equals(newInstance); | ||
| 104 | } | ||
| 105 | |||
| 106 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 107 | return new Expression(oldInstance, oldInstance.getClass(), | ||
| 108 | "new", new Object[]{oldInstance.toString()}); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | class ArrayPersistenceDelegate extends PersistenceDelegate { | ||
| 113 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 114 | return (newInstance != null && | ||
| 115 | oldInstance.getClass() == newInstance.getClass() && // Also ensures the subtype is correct. | ||
| 116 | Array.getLength(oldInstance) == Array.getLength(newInstance)); | ||
| 117 | } | ||
| 118 | |||
| 119 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 120 | // System.out.println("instantiate: " + type + " " + oldInstance); | ||
| 121 | Class oldClass = oldInstance.getClass(); | ||
| 122 | return new Expression(oldInstance, Array.class, "newInstance", | ||
| 123 | new Object[]{oldClass.getComponentType(), | ||
| 124 | new Integer(Array.getLength(oldInstance))}); | ||
| 125 | } | ||
| 126 | |||
| 127 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 128 | int n = Array.getLength(oldInstance); | ||
| 129 | for (int i = 0; i < n; i++) { | ||
| 130 | Object index = new Integer(i); | ||
| 131 | // Expression oldGetExp = new Expression(Array.class, "get", new Object[]{oldInstance, index}); | ||
| 132 | // Expression newGetExp = new Expression(Array.class, "get", new Object[]{newInstance, index}); | ||
| 133 | Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{index}); | ||
| 134 | Expression newGetExp = new Expression(newInstance, "get", new Object[]{index}); | ||
| 135 | try { | ||
| 136 | Object oldValue = oldGetExp.getValue(); | ||
| 137 | Object newValue = newGetExp.getValue(); | ||
| 138 | out.writeExpression(oldGetExp); | ||
| 139 | if (!Objects.equals(newValue, out.get(oldValue))) { | ||
| 140 | // System.out.println("Not equal: " + newGetExp + " != " + actualGetExp); | ||
| 141 | // invokeStatement(Array.class, "set", new Object[]{oldInstance, index, oldValue}, out); | ||
| 142 | DefaultPersistenceDelegate.invokeStatement(oldInstance, "set", new Object[]{index, oldValue}, out); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | catch (Exception e) { | ||
| 146 | // System.err.println("Warning:: failed to write: " + oldGetExp); | ||
| 147 | out.getExceptionListener().exceptionThrown(e); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | class ProxyPersistenceDelegate extends PersistenceDelegate { | ||
| 154 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 155 | Class type = oldInstance.getClass(); | ||
| 156 | java.lang.reflect.Proxy p = (java.lang.reflect.Proxy)oldInstance; | ||
| 157 | // This unappealing hack is not required but makes the | ||
| 158 | // representation of EventHandlers much more concise. | ||
| 159 | java.lang.reflect.InvocationHandler ih = java.lang.reflect.Proxy.getInvocationHandler(p); | ||
| 160 | if (ih instanceof EventHandler) { | ||
| 161 | EventHandler eh = (EventHandler)ih; | ||
| 162 | Vector args = new Vector(); | ||
| 163 | args.add(type.getInterfaces()[0]); | ||
| 164 | args.add(eh.getTarget()); | ||
| 165 | args.add(eh.getAction()); | ||
| 166 | if (eh.getEventPropertyName() != null) { | ||
| 167 | args.add(eh.getEventPropertyName()); | ||
| 168 | } | ||
| 169 | if (eh.getListenerMethodName() != null) { | ||
| 170 | args.setSize(4); | ||
| 171 | args.add(eh.getListenerMethodName()); | ||
| 172 | } | ||
| 173 | return new Expression(oldInstance, | ||
| 174 | EventHandler.class, | ||
| 175 | "create", | ||
| 176 | args.toArray()); | ||
| 177 | } | ||
| 178 | return new Expression(oldInstance, | ||
| 179 | java.lang.reflect.Proxy.class, | ||
| 180 | "newProxyInstance", | ||
| 181 | new Object[]{type.getClassLoader(), | ||
| 182 | type.getInterfaces(), | ||
| 183 | ih}); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | // Strings | ||
| 188 | class java_lang_String_PersistenceDelegate extends PersistenceDelegate { | ||
| 189 | protected Expression instantiate(Object oldInstance, Encoder out) { return null; } | ||
| 190 | |||
| 191 | public void writeObject(Object oldInstance, Encoder out) { | ||
| 192 | // System.out.println("NullPersistenceDelegate:writeObject " + oldInstance); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | // Classes | ||
| 197 | class java_lang_Class_PersistenceDelegate extends PersistenceDelegate { | ||
| 198 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 199 | return oldInstance.equals(newInstance); | ||
| 200 | } | ||
| 201 | |||
| 202 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 203 | Class c = (Class)oldInstance; | ||
| 204 | // As of 1.3 it is not possible to call Class.forName("int"), | ||
| 205 | // so we have to generate different code for primitive types. | ||
| 206 | // This is needed for arrays whose subtype may be primitive. | ||
| 207 | if (c.isPrimitive()) { | ||
| 208 | Field field = null; | ||
| 209 | try { | ||
| 210 | field = PrimitiveWrapperMap.getType(c.getName()).getDeclaredField("TYPE"); | ||
| 211 | } catch (NoSuchFieldException ex) { | ||
| 212 | System.err.println("Unknown primitive type: " + c); | ||
| 213 | } | ||
| 214 | return new Expression(oldInstance, field, "get", new Object[]{null}); | ||
| 215 | } | ||
| 216 | else if (oldInstance == String.class) { | ||
| 217 | return new Expression(oldInstance, "", "getClass", new Object[]{}); | ||
| 218 | } | ||
| 219 | else if (oldInstance == Class.class) { | ||
| 220 | return new Expression(oldInstance, String.class, "getClass", new Object[]{}); | ||
| 221 | } | ||
| 222 | else { | ||
| 223 | Expression newInstance = new Expression(oldInstance, Class.class, "forName", new Object[] { c.getName() }); | ||
| 224 | newInstance.loader = c.getClassLoader(); | ||
| 225 | return newInstance; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | // Fields | ||
| 231 | class java_lang_reflect_Field_PersistenceDelegate extends PersistenceDelegate { | ||
| 232 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 233 | return oldInstance.equals(newInstance); | ||
| 234 | } | ||
| 235 | |||
| 236 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 237 | Field f = (Field)oldInstance; | ||
| 238 | return new Expression(oldInstance, | ||
| 239 | f.getDeclaringClass(), | ||
| 240 | "getField", | ||
| 241 | new Object[]{f.getName()}); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | // Methods | ||
| 246 | class java_lang_reflect_Method_PersistenceDelegate extends PersistenceDelegate { | ||
| 247 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 248 | return oldInstance.equals(newInstance); | ||
| 249 | } | ||
| 250 | |||
| 251 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 252 | Method m = (Method)oldInstance; | ||
| 253 | return new Expression(oldInstance, | ||
| 254 | m.getDeclaringClass(), | ||
| 255 | "getMethod", | ||
| 256 | new Object[]{m.getName(), m.getParameterTypes()}); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | // Dates | ||
| 261 | |||
| 262 | /** | ||
| 263 | * The persistence delegate for <CODE>java.util.Date</CODE> classes. | ||
| 264 | * Do not extend DefaultPersistenceDelegate to improve performance and | ||
| 265 | * to avoid problems with <CODE>java.sql.Date</CODE>, | ||
| 266 | * <CODE>java.sql.Time</CODE> and <CODE>java.sql.Timestamp</CODE>. | ||
| 267 | * | ||
| 268 | * @author Sergey A. Malenkov | ||
| 269 | */ | ||
| 270 | class java_util_Date_PersistenceDelegate extends PersistenceDelegate { | ||
| 271 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 272 | if (!super.mutatesTo(oldInstance, newInstance)) { | ||
| 273 | return false; | ||
| 274 | } | ||
| 275 | Date oldDate = (Date)oldInstance; | ||
| 276 | Date newDate = (Date)newInstance; | ||
| 277 | |||
| 278 | return oldDate.getTime() == newDate.getTime(); | ||
| 279 | } | ||
| 280 | |||
| 281 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 282 | Date date = (Date)oldInstance; | ||
| 283 | return new Expression(date, date.getClass(), "new", new Object[] {date.getTime()}); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | /** | ||
| 288 | * The persistence delegate for <CODE>java.sql.Timestamp</CODE> classes. | ||
| 289 | * It supports nanoseconds. | ||
| 290 | * | ||
| 291 | * @author Sergey A. Malenkov | ||
| 292 | */ | ||
| 293 | final class java_sql_Timestamp_PersistenceDelegate extends java_util_Date_PersistenceDelegate { | ||
| 294 | private static final Method getNanosMethod = getNanosMethod(); | ||
| 295 | |||
| 296 | private static Method getNanosMethod() { | ||
| 297 | try { | ||
| 298 | Class<?> c = Class.forName("java.sql.Timestamp", true, null); | ||
| 299 | return c.getMethod("getNanos"); | ||
| 300 | } catch (ClassNotFoundException e) { | ||
| 301 | return null; | ||
| 302 | } catch (NoSuchMethodException e) { | ||
| 303 | throw new AssertionError(e); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | /** | ||
| 308 | * Invoke Timstamp getNanos. | ||
| 309 | */ | ||
| 310 | private static int getNanos(Object obj) { | ||
| 311 | if (getNanosMethod == null) | ||
| 312 | throw new AssertionError("Should not get here"); | ||
| 313 | try { | ||
| 314 | return (Integer)getNanosMethod.invoke(obj); | ||
| 315 | } catch (InvocationTargetException e) { | ||
| 316 | Throwable cause = e.getCause(); | ||
| 317 | if (cause instanceof RuntimeException) | ||
| 318 | throw (RuntimeException)cause; | ||
| 319 | if (cause instanceof Error) | ||
| 320 | throw (Error)cause; | ||
| 321 | throw new AssertionError(e); | ||
| 322 | } catch (IllegalAccessException iae) { | ||
| 323 | throw new AssertionError(iae); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 328 | // assumes oldInstance and newInstance are Timestamps | ||
| 329 | int nanos = getNanos(oldInstance); | ||
| 330 | if (nanos != getNanos(newInstance)) { | ||
| 331 | out.writeStatement(new Statement(oldInstance, "setNanos", new Object[] {nanos})); | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 336 | // Collections | ||
| 337 | |||
| 338 | /* | ||
| 339 | The Hashtable and AbstractMap classes have no common ancestor yet may | ||
| 340 | be handled with a single persistence delegate: one which uses the methods | ||
| 341 | of the Map insterface exclusively. Attatching the persistence delegates | ||
| 342 | to the interfaces themselves is fraught however since, in the case of | ||
| 343 | the Map, both the AbstractMap and HashMap classes are declared to | ||
| 344 | implement the Map interface, leaving the obvious implementation prone | ||
| 345 | to repeating their initialization. These issues and questions around | ||
| 346 | the ordering of delegates attached to interfaces have lead us to | ||
| 347 | ignore any delegates attached to interfaces and force all persistence | ||
| 348 | delegates to be registered with concrete classes. | ||
| 349 | */ | ||
| 350 | |||
| 351 | /** | ||
| 352 | * The base class for persistence delegates for inner classes | ||
| 353 | * that can be created using {@link Collections}. | ||
| 354 | * | ||
| 355 | * @author Sergey A. Malenkov | ||
| 356 | */ | ||
| 357 | abstract class java_util_Collections extends PersistenceDelegate { | ||
| 358 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 359 | if (!super.mutatesTo(oldInstance, newInstance)) { | ||
| 360 | return false; | ||
| 361 | } | ||
| 362 | if ((oldInstance instanceof List) || (oldInstance instanceof Set) || (oldInstance instanceof Map)) { | ||
| 363 | return oldInstance.equals(newInstance); | ||
| 364 | } | ||
| 365 | Collection oldC = (Collection) oldInstance; | ||
| 366 | Collection newC = (Collection) newInstance; | ||
| 367 | return (oldC.size() == newC.size()) && oldC.containsAll(newC); | ||
| 368 | } | ||
| 369 | |||
| 370 | static final class EmptyList_PersistenceDelegate extends java_util_Collections { | ||
| 371 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 372 | return new Expression(oldInstance, Collections.class, "emptyList", null); | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | static final class EmptySet_PersistenceDelegate extends java_util_Collections { | ||
| 377 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 378 | return new Expression(oldInstance, Collections.class, "emptySet", null); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | static final class EmptyMap_PersistenceDelegate extends java_util_Collections { | ||
| 383 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 384 | return new Expression(oldInstance, Collections.class, "emptyMap", null); | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | static final class SingletonList_PersistenceDelegate extends java_util_Collections { | ||
| 389 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 390 | List list = (List) oldInstance; | ||
| 391 | return new Expression(oldInstance, Collections.class, "singletonList", new Object[]{list.get(0)}); | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | static final class SingletonSet_PersistenceDelegate extends java_util_Collections { | ||
| 396 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 397 | Set set = (Set) oldInstance; | ||
| 398 | return new Expression(oldInstance, Collections.class, "singleton", new Object[]{set.iterator().next()}); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | static final class SingletonMap_PersistenceDelegate extends java_util_Collections { | ||
| 403 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 404 | Map map = (Map) oldInstance; | ||
| 405 | Object key = map.keySet().iterator().next(); | ||
| 406 | return new Expression(oldInstance, Collections.class, "singletonMap", new Object[]{key, map.get(key)}); | ||
| 407 | } | ||
| 408 | } | ||
| 409 | |||
| 410 | static final class UnmodifiableCollection_PersistenceDelegate extends java_util_Collections { | ||
| 411 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 412 | List list = new ArrayList((Collection) oldInstance); | ||
| 413 | return new Expression(oldInstance, Collections.class, "unmodifiableCollection", new Object[]{list}); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | static final class UnmodifiableList_PersistenceDelegate extends java_util_Collections { | ||
| 418 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 419 | List list = new LinkedList((Collection) oldInstance); | ||
| 420 | return new Expression(oldInstance, Collections.class, "unmodifiableList", new Object[]{list}); | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | static final class UnmodifiableRandomAccessList_PersistenceDelegate extends java_util_Collections { | ||
| 425 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 426 | List list = new ArrayList((Collection) oldInstance); | ||
| 427 | return new Expression(oldInstance, Collections.class, "unmodifiableList", new Object[]{list}); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | static final class UnmodifiableSet_PersistenceDelegate extends java_util_Collections { | ||
| 432 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 433 | Set set = new HashSet((Set) oldInstance); | ||
| 434 | return new Expression(oldInstance, Collections.class, "unmodifiableSet", new Object[]{set}); | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | static final class UnmodifiableSortedSet_PersistenceDelegate extends java_util_Collections { | ||
| 439 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 440 | SortedSet set = new TreeSet((SortedSet) oldInstance); | ||
| 441 | return new Expression(oldInstance, Collections.class, "unmodifiableSortedSet", new Object[]{set}); | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | static final class UnmodifiableMap_PersistenceDelegate extends java_util_Collections { | ||
| 446 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 447 | Map map = new HashMap((Map) oldInstance); | ||
| 448 | return new Expression(oldInstance, Collections.class, "unmodifiableMap", new Object[]{map}); | ||
| 449 | } | ||
| 450 | } | ||
| 451 | |||
| 452 | static final class UnmodifiableSortedMap_PersistenceDelegate extends java_util_Collections { | ||
| 453 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 454 | SortedMap map = new TreeMap((SortedMap) oldInstance); | ||
| 455 | return new Expression(oldInstance, Collections.class, "unmodifiableSortedMap", new Object[]{map}); | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | static final class SynchronizedCollection_PersistenceDelegate extends java_util_Collections { | ||
| 460 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 461 | List list = new ArrayList((Collection) oldInstance); | ||
| 462 | return new Expression(oldInstance, Collections.class, "synchronizedCollection", new Object[]{list}); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | static final class SynchronizedList_PersistenceDelegate extends java_util_Collections { | ||
| 467 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 468 | List list = new LinkedList((Collection) oldInstance); | ||
| 469 | return new Expression(oldInstance, Collections.class, "synchronizedList", new Object[]{list}); | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | static final class SynchronizedRandomAccessList_PersistenceDelegate extends java_util_Collections { | ||
| 474 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 475 | List list = new ArrayList((Collection) oldInstance); | ||
| 476 | return new Expression(oldInstance, Collections.class, "synchronizedList", new Object[]{list}); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | static final class SynchronizedSet_PersistenceDelegate extends java_util_Collections { | ||
| 481 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 482 | Set set = new HashSet((Set) oldInstance); | ||
| 483 | return new Expression(oldInstance, Collections.class, "synchronizedSet", new Object[]{set}); | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | static final class SynchronizedSortedSet_PersistenceDelegate extends java_util_Collections { | ||
| 488 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 489 | SortedSet set = new TreeSet((SortedSet) oldInstance); | ||
| 490 | return new Expression(oldInstance, Collections.class, "synchronizedSortedSet", new Object[]{set}); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | |||
| 494 | static final class SynchronizedMap_PersistenceDelegate extends java_util_Collections { | ||
| 495 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 496 | Map map = new HashMap((Map) oldInstance); | ||
| 497 | return new Expression(oldInstance, Collections.class, "synchronizedMap", new Object[]{map}); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | static final class SynchronizedSortedMap_PersistenceDelegate extends java_util_Collections { | ||
| 502 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 503 | SortedMap map = new TreeMap((SortedMap) oldInstance); | ||
| 504 | return new Expression(oldInstance, Collections.class, "synchronizedSortedMap", new Object[]{map}); | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | static final class CheckedCollection_PersistenceDelegate extends java_util_Collections { | ||
| 509 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 510 | Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); | ||
| 511 | List list = new ArrayList((Collection) oldInstance); | ||
| 512 | return new Expression(oldInstance, Collections.class, "checkedCollection", new Object[]{list, type}); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | static final class CheckedList_PersistenceDelegate extends java_util_Collections { | ||
| 517 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 518 | Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); | ||
| 519 | List list = new LinkedList((Collection) oldInstance); | ||
| 520 | return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type}); | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | static final class CheckedRandomAccessList_PersistenceDelegate extends java_util_Collections { | ||
| 525 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 526 | Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); | ||
| 527 | List list = new ArrayList((Collection) oldInstance); | ||
| 528 | return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type}); | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | static final class CheckedSet_PersistenceDelegate extends java_util_Collections { | ||
| 533 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 534 | Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); | ||
| 535 | Set set = new HashSet((Set) oldInstance); | ||
| 536 | return new Expression(oldInstance, Collections.class, "checkedSet", new Object[]{set, type}); | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | static final class CheckedSortedSet_PersistenceDelegate extends java_util_Collections { | ||
| 541 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 542 | Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); | ||
| 543 | SortedSet set = new TreeSet((SortedSet) oldInstance); | ||
| 544 | return new Expression(oldInstance, Collections.class, "checkedSortedSet", new Object[]{set, type}); | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | static final class CheckedMap_PersistenceDelegate extends java_util_Collections { | ||
| 549 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 550 | Object keyType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType"); | ||
| 551 | Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType"); | ||
| 552 | Map map = new HashMap((Map) oldInstance); | ||
| 553 | return new Expression(oldInstance, Collections.class, "checkedMap", new Object[]{map, keyType, valueType}); | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | static final class CheckedSortedMap_PersistenceDelegate extends java_util_Collections { | ||
| 558 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 559 | Object keyType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType"); | ||
| 560 | Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType"); | ||
| 561 | SortedMap map = new TreeMap((SortedMap) oldInstance); | ||
| 562 | return new Expression(oldInstance, Collections.class, "checkedSortedMap", new Object[]{map, keyType, valueType}); | ||
| 563 | } | ||
| 564 | } | ||
| 565 | } | ||
| 566 | |||
| 567 | /** | ||
| 568 | * The persistence delegate for <CODE>java.util.EnumMap</CODE> classes. | ||
| 569 | * | ||
| 570 | * @author Sergey A. Malenkov | ||
| 571 | */ | ||
| 572 | class java_util_EnumMap_PersistenceDelegate extends PersistenceDelegate { | ||
| 573 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 574 | return super.mutatesTo(oldInstance, newInstance) && (getType(oldInstance) == getType(newInstance)); | ||
| 575 | } | ||
| 576 | |||
| 577 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 578 | return new Expression(oldInstance, EnumMap.class, "new", new Object[] {getType(oldInstance)}); | ||
| 579 | } | ||
| 580 | |||
| 581 | private static Object getType(Object instance) { | ||
| 582 | return MetaData.getPrivateFieldValue(instance, "java.util.EnumMap.keyType"); | ||
| 583 | } | ||
| 584 | } | ||
| 585 | |||
| 586 | /** | ||
| 587 | * The persistence delegate for <CODE>java.util.EnumSet</CODE> classes. | ||
| 588 | * | ||
| 589 | * @author Sergey A. Malenkov | ||
| 590 | */ | ||
| 591 | class java_util_EnumSet_PersistenceDelegate extends PersistenceDelegate { | ||
| 592 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 593 | return super.mutatesTo(oldInstance, newInstance) && (getType(oldInstance) == getType(newInstance)); | ||
| 594 | } | ||
| 595 | |||
| 596 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 597 | return new Expression(oldInstance, EnumSet.class, "noneOf", new Object[] {getType(oldInstance)}); | ||
| 598 | } | ||
| 599 | |||
| 600 | private static Object getType(Object instance) { | ||
| 601 | return MetaData.getPrivateFieldValue(instance, "java.util.EnumSet.elementType"); | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 605 | // Collection | ||
| 606 | class java_util_Collection_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 607 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 608 | java.util.Collection oldO = (java.util.Collection)oldInstance; | ||
| 609 | java.util.Collection newO = (java.util.Collection)newInstance; | ||
| 610 | |||
| 611 | if (newO.size() != 0) { | ||
| 612 | invokeStatement(oldInstance, "clear", new Object[]{}, out); | ||
| 613 | } | ||
| 614 | for (Iterator i = oldO.iterator(); i.hasNext();) { | ||
| 615 | invokeStatement(oldInstance, "add", new Object[]{i.next()}, out); | ||
| 616 | } | ||
| 617 | } | ||
| 618 | } | ||
| 619 | |||
| 620 | // List | ||
| 621 | class java_util_List_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 622 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 623 | java.util.List oldO = (java.util.List)oldInstance; | ||
| 624 | java.util.List newO = (java.util.List)newInstance; | ||
| 625 | int oldSize = oldO.size(); | ||
| 626 | int newSize = (newO == null) ? 0 : newO.size(); | ||
| 627 | if (oldSize < newSize) { | ||
| 628 | invokeStatement(oldInstance, "clear", new Object[]{}, out); | ||
| 629 | newSize = 0; | ||
| 630 | } | ||
| 631 | for (int i = 0; i < newSize; i++) { | ||
| 632 | Object index = new Integer(i); | ||
| 633 | |||
| 634 | Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{index}); | ||
| 635 | Expression newGetExp = new Expression(newInstance, "get", new Object[]{index}); | ||
| 636 | try { | ||
| 637 | Object oldValue = oldGetExp.getValue(); | ||
| 638 | Object newValue = newGetExp.getValue(); | ||
| 639 | out.writeExpression(oldGetExp); | ||
| 640 | if (!Objects.equals(newValue, out.get(oldValue))) { | ||
| 641 | invokeStatement(oldInstance, "set", new Object[]{index, oldValue}, out); | ||
| 642 | } | ||
| 643 | } | ||
| 644 | catch (Exception e) { | ||
| 645 | out.getExceptionListener().exceptionThrown(e); | ||
| 646 | } | ||
| 647 | } | ||
| 648 | for (int i = newSize; i < oldSize; i++) { | ||
| 649 | invokeStatement(oldInstance, "add", new Object[]{oldO.get(i)}, out); | ||
| 650 | } | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | |||
| 655 | // Map | ||
| 656 | class java_util_Map_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 657 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 658 | // System.out.println("Initializing: " + newInstance); | ||
| 659 | java.util.Map oldMap = (java.util.Map)oldInstance; | ||
| 660 | java.util.Map newMap = (java.util.Map)newInstance; | ||
| 661 | // Remove the new elements. | ||
| 662 | // Do this first otherwise we undo the adding work. | ||
| 663 | if (newMap != null) { | ||
| 664 | for (Object newKey : newMap.keySet().toArray()) { | ||
| 665 | // PENDING: This "key" is not in the right environment. | ||
| 666 | if (!oldMap.containsKey(newKey)) { | ||
| 667 | invokeStatement(oldInstance, "remove", new Object[]{newKey}, out); | ||
| 668 | } | ||
| 669 | } | ||
| 670 | } | ||
| 671 | // Add the new elements. | ||
| 672 | for ( Object oldKey : oldMap.keySet() ) { | ||
| 673 | Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{oldKey}); | ||
| 674 | // Pending: should use newKey. | ||
| 675 | Expression newGetExp = new Expression(newInstance, "get", new Object[]{oldKey}); | ||
| 676 | try { | ||
| 677 | Object oldValue = oldGetExp.getValue(); | ||
| 678 | Object newValue = newGetExp.getValue(); | ||
| 679 | out.writeExpression(oldGetExp); | ||
| 680 | if (!Objects.equals(newValue, out.get(oldValue))) { | ||
| 681 | invokeStatement(oldInstance, "put", new Object[]{oldKey, oldValue}, out); | ||
| 682 | } else if ((newValue == null) && !newMap.containsKey(oldKey)) { | ||
| 683 | // put oldValue(=null?) if oldKey is absent in newMap | ||
| 684 | invokeStatement(oldInstance, "put", new Object[]{oldKey, oldValue}, out); | ||
| 685 | } | ||
| 686 | } | ||
| 687 | catch (Exception e) { | ||
| 688 | out.getExceptionListener().exceptionThrown(e); | ||
| 689 | } | ||
| 690 | } | ||
| 691 | } | ||
| 692 | } | ||
| 693 | |||
| 694 | class java_util_AbstractCollection_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {} | ||
| 695 | class java_util_AbstractList_PersistenceDelegate extends java_util_List_PersistenceDelegate {} | ||
| 696 | class java_util_AbstractMap_PersistenceDelegate extends java_util_Map_PersistenceDelegate {} | ||
| 697 | class java_util_Hashtable_PersistenceDelegate extends java_util_Map_PersistenceDelegate {} | ||
| 698 | |||
| 699 | |||
| 700 | // Beans | ||
| 701 | class java_beans_beancontext_BeanContextSupport_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {} | ||
| 702 | |||
| 703 | // AWT | ||
| 704 | |||
| 705 | /** | ||
| 706 | * The persistence delegate for {@link Insets}. | ||
| 707 | * It is impossible to use {@link DefaultPersistenceDelegate} | ||
| 708 | * because this class does not have any properties. | ||
| 709 | * | ||
| 710 | * @author Sergey A. Malenkov | ||
| 711 | */ | ||
| 712 | final class java_awt_Insets_PersistenceDelegate extends PersistenceDelegate { | ||
| 713 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 714 | return oldInstance.equals(newInstance); | ||
| 715 | } | ||
| 716 | |||
| 717 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 718 | Insets insets = (Insets) oldInstance; | ||
| 719 | Object[] args = new Object[] { | ||
| 720 | insets.top, | ||
| 721 | insets.left, | ||
| 722 | insets.bottom, | ||
| 723 | insets.right, | ||
| 724 | }; | ||
| 725 | return new Expression(insets, insets.getClass(), "new", args); | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | /** | ||
| 730 | * The persistence delegate for {@link Font}. | ||
| 731 | * It is impossible to use {@link DefaultPersistenceDelegate} | ||
| 732 | * because size of the font can be float value. | ||
| 733 | * | ||
| 734 | * @author Sergey A. Malenkov | ||
| 735 | */ | ||
| 736 | final class java_awt_Font_PersistenceDelegate extends PersistenceDelegate { | ||
| 737 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 738 | return oldInstance.equals(newInstance); | ||
| 739 | } | ||
| 740 | |||
| 741 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 742 | Font font = (Font) oldInstance; | ||
| 743 | |||
| 744 | int count = 0; | ||
| 745 | String family = null; | ||
| 746 | int style = Font.PLAIN; | ||
| 747 | int size = 12; | ||
| 748 | |||
| 749 | Map basic = font.getAttributes(); | ||
| 750 | Map clone = new HashMap(basic.size()); | ||
| 751 | for (Object key : basic.keySet()) { | ||
| 752 | Object value = basic.get(key); | ||
| 753 | if (value != null) { | ||
| 754 | clone.put(key, value); | ||
| 755 | } | ||
| 756 | if (key == TextAttribute.FAMILY) { | ||
| 757 | if (value instanceof String) { | ||
| 758 | count++; | ||
| 759 | family = (String) value; | ||
| 760 | } | ||
| 761 | } | ||
| 762 | else if (key == TextAttribute.WEIGHT) { | ||
| 763 | if (TextAttribute.WEIGHT_REGULAR.equals(value)) { | ||
| 764 | count++; | ||
| 765 | } else if (TextAttribute.WEIGHT_BOLD.equals(value)) { | ||
| 766 | count++; | ||
| 767 | style |= Font.BOLD; | ||
| 768 | } | ||
| 769 | } | ||
| 770 | else if (key == TextAttribute.POSTURE) { | ||
| 771 | if (TextAttribute.POSTURE_REGULAR.equals(value)) { | ||
| 772 | count++; | ||
| 773 | } else if (TextAttribute.POSTURE_OBLIQUE.equals(value)) { | ||
| 774 | count++; | ||
| 775 | style |= Font.ITALIC; | ||
| 776 | } | ||
| 777 | } else if (key == TextAttribute.SIZE) { | ||
| 778 | if (value instanceof Number) { | ||
| 779 | Number number = (Number) value; | ||
| 780 | size = number.intValue(); | ||
| 781 | if (size == number.floatValue()) { | ||
| 782 | count++; | ||
| 783 | } | ||
| 784 | } | ||
| 785 | } | ||
| 786 | } | ||
| 787 | Class type = font.getClass(); | ||
| 788 | if (count == clone.size()) { | ||
| 789 | return new Expression(font, type, "new", new Object[]{family, style, size}); | ||
| 790 | } | ||
| 791 | if (type == Font.class) { | ||
| 792 | return new Expression(font, type, "getFont", new Object[]{clone}); | ||
| 793 | } | ||
| 794 | return new Expression(font, type, "new", new Object[]{Font.getFont(clone)}); | ||
| 795 | } | ||
| 796 | } | ||
| 797 | |||
| 798 | /** | ||
| 799 | * The persistence delegate for {@link AWTKeyStroke}. | ||
| 800 | * It is impossible to use {@link DefaultPersistenceDelegate} | ||
| 801 | * because this class have no public constructor. | ||
| 802 | * | ||
| 803 | * @author Sergey A. Malenkov | ||
| 804 | */ | ||
| 805 | final class java_awt_AWTKeyStroke_PersistenceDelegate extends PersistenceDelegate { | ||
| 806 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 807 | return oldInstance.equals(newInstance); | ||
| 808 | } | ||
| 809 | |||
| 810 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 811 | AWTKeyStroke key = (AWTKeyStroke) oldInstance; | ||
| 812 | |||
| 813 | char ch = key.getKeyChar(); | ||
| 814 | int code = key.getKeyCode(); | ||
| 815 | int mask = key.getModifiers(); | ||
| 816 | boolean onKeyRelease = key.isOnKeyRelease(); | ||
| 817 | |||
| 818 | Object[] args = null; | ||
| 819 | if (ch == KeyEvent.CHAR_UNDEFINED) { | ||
| 820 | args = !onKeyRelease | ||
| 821 | ? new Object[]{code, mask} | ||
| 822 | : new Object[]{code, mask, onKeyRelease}; | ||
| 823 | } else if (code == KeyEvent.VK_UNDEFINED) { | ||
| 824 | if (!onKeyRelease) { | ||
| 825 | args = (mask == 0) | ||
| 826 | ? new Object[]{ch} | ||
| 827 | : new Object[]{ch, mask}; | ||
| 828 | } else if (mask == 0) { | ||
| 829 | args = new Object[]{ch, onKeyRelease}; | ||
| 830 | } | ||
| 831 | } | ||
| 832 | if (args == null) { | ||
| 833 | throw new IllegalStateException("Unsupported KeyStroke: " + key); | ||
| 834 | } | ||
| 835 | Class type = key.getClass(); | ||
| 836 | String name = type.getName(); | ||
| 837 | // get short name of the class | ||
| 838 | int index = name.lastIndexOf('.') + 1; | ||
| 839 | if (index > 0) { | ||
| 840 | name = name.substring(index); | ||
| 841 | } | ||
| 842 | return new Expression( key, type, "get" + name, args ); | ||
| 843 | } | ||
| 844 | } | ||
| 845 | |||
| 846 | class StaticFieldsPersistenceDelegate extends PersistenceDelegate { | ||
| 847 | protected void installFields(Encoder out, Class<?> cls) { | ||
| 848 | Field fields[] = cls.getFields(); | ||
| 849 | for(int i = 0; i < fields.length; i++) { | ||
| 850 | Field field = fields[i]; | ||
| 851 | // Don't install primitives, their identity will not be preserved | ||
| 852 | // by wrapping. | ||
| 853 | if (Object.class.isAssignableFrom(field.getType())) { | ||
| 854 | out.writeExpression(new Expression(field, "get", new Object[]{null})); | ||
| 855 | } | ||
| 856 | } | ||
| 857 | } | ||
| 858 | |||
| 859 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 860 | throw new RuntimeException("Unrecognized instance: " + oldInstance); | ||
| 861 | } | ||
| 862 | |||
| 863 | public void writeObject(Object oldInstance, Encoder out) { | ||
| 864 | if (out.getAttribute(this) == null) { | ||
| 865 | out.setAttribute(this, Boolean.TRUE); | ||
| 866 | installFields(out, oldInstance.getClass()); | ||
| 867 | } | ||
| 868 | super.writeObject(oldInstance, out); | ||
| 869 | } | ||
| 870 | } | ||
| 871 | |||
| 872 | // SystemColor | ||
| 873 | class java_awt_SystemColor_PersistenceDelegate extends StaticFieldsPersistenceDelegate {} | ||
| 874 | |||
| 875 | // TextAttribute | ||
| 876 | class java_awt_font_TextAttribute_PersistenceDelegate extends StaticFieldsPersistenceDelegate {} | ||
| 877 | |||
| 878 | // MenuShortcut | ||
| 879 | class java_awt_MenuShortcut_PersistenceDelegate extends PersistenceDelegate { | ||
| 880 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 881 | return oldInstance.equals(newInstance); | ||
| 882 | } | ||
| 883 | |||
| 884 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 885 | java.awt.MenuShortcut m = (java.awt.MenuShortcut)oldInstance; | ||
| 886 | return new Expression(oldInstance, m.getClass(), "new", | ||
| 887 | new Object[]{new Integer(m.getKey()), Boolean.valueOf(m.usesShiftModifier())}); | ||
| 888 | } | ||
| 889 | } | ||
| 890 | |||
| 891 | // Component | ||
| 892 | class java_awt_Component_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 893 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 894 | super.initialize(type, oldInstance, newInstance, out); | ||
| 895 | java.awt.Component c = (java.awt.Component)oldInstance; | ||
| 896 | java.awt.Component c2 = (java.awt.Component)newInstance; | ||
| 897 | // The "background", "foreground" and "font" properties. | ||
| 898 | // The foreground and font properties of Windows change from | ||
| 899 | // null to defined values after the Windows are made visible - | ||
| 900 | // special case them for now. | ||
| 901 | if (!(oldInstance instanceof java.awt.Window)) { | ||
| 902 | Object oldBackground = c.isBackgroundSet() ? c.getBackground() : null; | ||
| 903 | Object newBackground = c2.isBackgroundSet() ? c2.getBackground() : null; | ||
| 904 | if (!Objects.equals(oldBackground, newBackground)) { | ||
| 905 | invokeStatement(oldInstance, "setBackground", new Object[] { oldBackground }, out); | ||
| 906 | } | ||
| 907 | Object oldForeground = c.isForegroundSet() ? c.getForeground() : null; | ||
| 908 | Object newForeground = c2.isForegroundSet() ? c2.getForeground() : null; | ||
| 909 | if (!Objects.equals(oldForeground, newForeground)) { | ||
| 910 | invokeStatement(oldInstance, "setForeground", new Object[] { oldForeground }, out); | ||
| 911 | } | ||
| 912 | Object oldFont = c.isFontSet() ? c.getFont() : null; | ||
| 913 | Object newFont = c2.isFontSet() ? c2.getFont() : null; | ||
| 914 | if (!Objects.equals(oldFont, newFont)) { | ||
| 915 | invokeStatement(oldInstance, "setFont", new Object[] { oldFont }, out); | ||
| 916 | } | ||
| 917 | } | ||
| 918 | |||
| 919 | // Bounds | ||
| 920 | java.awt.Container p = c.getParent(); | ||
| 921 | if (p == null || p.getLayout() == null) { | ||
| 922 | // Use the most concise construct. | ||
| 923 | boolean locationCorrect = c.getLocation().equals(c2.getLocation()); | ||
| 924 | boolean sizeCorrect = c.getSize().equals(c2.getSize()); | ||
| 925 | if (!locationCorrect && !sizeCorrect) { | ||
| 926 | invokeStatement(oldInstance, "setBounds", new Object[]{c.getBounds()}, out); | ||
| 927 | } | ||
| 928 | else if (!locationCorrect) { | ||
| 929 | invokeStatement(oldInstance, "setLocation", new Object[]{c.getLocation()}, out); | ||
| 930 | } | ||
| 931 | else if (!sizeCorrect) { | ||
| 932 | invokeStatement(oldInstance, "setSize", new Object[]{c.getSize()}, out); | ||
| 933 | } | ||
| 934 | } | ||
| 935 | } | ||
| 936 | } | ||
| 937 | |||
| 938 | // Container | ||
| 939 | class java_awt_Container_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 940 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 941 | super.initialize(type, oldInstance, newInstance, out); | ||
| 942 | // Ignore the children of a JScrollPane. | ||
| 943 | // Pending(milne) find a better way to do this. | ||
| 944 | if (oldInstance instanceof javax.swing.JScrollPane) { | ||
| 945 | return; | ||
| 946 | } | ||
| 947 | java.awt.Container oldC = (java.awt.Container)oldInstance; | ||
| 948 | java.awt.Component[] oldChildren = oldC.getComponents(); | ||
| 949 | java.awt.Container newC = (java.awt.Container)newInstance; | ||
| 950 | java.awt.Component[] newChildren = (newC == null) ? new java.awt.Component[0] : newC.getComponents(); | ||
| 951 | |||
| 952 | BorderLayout layout = ( oldC.getLayout() instanceof BorderLayout ) | ||
| 953 | ? ( BorderLayout )oldC.getLayout() | ||
| 954 | : null; | ||
| 955 | |||
| 956 | JLayeredPane oldLayeredPane = (oldInstance instanceof JLayeredPane) | ||
| 957 | ? (JLayeredPane) oldInstance | ||
| 958 | : null; | ||
| 959 | |||
| 960 | // Pending. Assume all the new children are unaltered. | ||
| 961 | for(int i = newChildren.length; i < oldChildren.length; i++) { | ||
| 962 | Object[] args = ( layout != null ) | ||
| 963 | ? new Object[] {oldChildren[i], layout.getConstraints( oldChildren[i] )} | ||
| 964 | : (oldLayeredPane != null) | ||
| 965 | ? new Object[] {oldChildren[i], oldLayeredPane.getLayer(oldChildren[i]), Integer.valueOf(-1)} | ||
| 966 | : new Object[] {oldChildren[i]}; | ||
| 967 | |||
| 968 | invokeStatement(oldInstance, "add", args, out); | ||
| 969 | } | ||
| 970 | } | ||
| 971 | } | ||
| 972 | |||
| 973 | // Choice | ||
| 974 | class java_awt_Choice_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 975 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 976 | super.initialize(type, oldInstance, newInstance, out); | ||
| 977 | java.awt.Choice m = (java.awt.Choice)oldInstance; | ||
| 978 | java.awt.Choice n = (java.awt.Choice)newInstance; | ||
| 979 | for (int i = n.getItemCount(); i < m.getItemCount(); i++) { | ||
| 980 | invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out); | ||
| 981 | } | ||
| 982 | } | ||
| 983 | } | ||
| 984 | |||
| 985 | // Menu | ||
| 986 | class java_awt_Menu_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 987 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 988 | super.initialize(type, oldInstance, newInstance, out); | ||
| 989 | java.awt.Menu m = (java.awt.Menu)oldInstance; | ||
| 990 | java.awt.Menu n = (java.awt.Menu)newInstance; | ||
| 991 | for (int i = n.getItemCount(); i < m.getItemCount(); i++) { | ||
| 992 | invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out); | ||
| 993 | } | ||
| 994 | } | ||
| 995 | } | ||
| 996 | |||
| 997 | // MenuBar | ||
| 998 | class java_awt_MenuBar_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 999 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1000 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1001 | java.awt.MenuBar m = (java.awt.MenuBar)oldInstance; | ||
| 1002 | java.awt.MenuBar n = (java.awt.MenuBar)newInstance; | ||
| 1003 | for (int i = n.getMenuCount(); i < m.getMenuCount(); i++) { | ||
| 1004 | invokeStatement(oldInstance, "add", new Object[]{m.getMenu(i)}, out); | ||
| 1005 | } | ||
| 1006 | } | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | // List | ||
| 1010 | class java_awt_List_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1011 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1012 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1013 | java.awt.List m = (java.awt.List)oldInstance; | ||
| 1014 | java.awt.List n = (java.awt.List)newInstance; | ||
| 1015 | for (int i = n.getItemCount(); i < m.getItemCount(); i++) { | ||
| 1016 | invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out); | ||
| 1017 | } | ||
| 1018 | } | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | |||
| 1022 | // LayoutManagers | ||
| 1023 | |||
| 1024 | // BorderLayout | ||
| 1025 | class java_awt_BorderLayout_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1026 | private static final String[] CONSTRAINTS = { | ||
| 1027 | BorderLayout.NORTH, | ||
| 1028 | BorderLayout.SOUTH, | ||
| 1029 | BorderLayout.EAST, | ||
| 1030 | BorderLayout.WEST, | ||
| 1031 | BorderLayout.CENTER, | ||
| 1032 | BorderLayout.PAGE_START, | ||
| 1033 | BorderLayout.PAGE_END, | ||
| 1034 | BorderLayout.LINE_START, | ||
| 1035 | BorderLayout.LINE_END, | ||
| 1036 | }; | ||
| 1037 | @Override | ||
| 1038 | protected void initialize(Class<?> type, Object oldInstance, | ||
| 1039 | Object newInstance, Encoder out) { | ||
| 1040 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1041 | BorderLayout oldLayout = (BorderLayout) oldInstance; | ||
| 1042 | BorderLayout newLayout = (BorderLayout) newInstance; | ||
| 1043 | for (String constraints : CONSTRAINTS) { | ||
| 1044 | Object oldC = oldLayout.getLayoutComponent(constraints); | ||
| 1045 | Object newC = newLayout.getLayoutComponent(constraints); | ||
| 1046 | // Pending, assume any existing elements are OK. | ||
| 1047 | if (oldC != null && newC == null) { | ||
| 1048 | invokeStatement(oldInstance, "addLayoutComponent", | ||
| 1049 | new Object[] { oldC, constraints }, out); | ||
| 1050 | } | ||
| 1051 | } | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | // CardLayout | ||
| 1056 | class java_awt_CardLayout_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1057 | protected void initialize(Class<?> type, Object oldInstance, | ||
| 1058 | Object newInstance, Encoder out) { | ||
| 1059 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1060 | Hashtable tab = (Hashtable)ReflectionUtils.getPrivateField(oldInstance, | ||
| 1061 | java.awt.CardLayout.class, | ||
| 1062 | "tab", | ||
| 1063 | out.getExceptionListener()); | ||
| 1064 | if (tab != null) { | ||
| 1065 | for(Enumeration e = tab.keys(); e.hasMoreElements();) { | ||
| 1066 | Object child = e.nextElement(); | ||
| 1067 | invokeStatement(oldInstance, "addLayoutComponent", | ||
| 1068 | new Object[]{child, (String)tab.get(child)}, out); | ||
| 1069 | } | ||
| 1070 | } | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | // GridBagLayout | ||
| 1075 | class java_awt_GridBagLayout_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1076 | protected void initialize(Class<?> type, Object oldInstance, | ||
| 1077 | Object newInstance, Encoder out) { | ||
| 1078 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1079 | Hashtable comptable = (Hashtable)ReflectionUtils.getPrivateField(oldInstance, | ||
| 1080 | java.awt.GridBagLayout.class, | ||
| 1081 | "comptable", | ||
| 1082 | out.getExceptionListener()); | ||
| 1083 | if (comptable != null) { | ||
| 1084 | for(Enumeration e = comptable.keys(); e.hasMoreElements();) { | ||
| 1085 | Object child = e.nextElement(); | ||
| 1086 | invokeStatement(oldInstance, "addLayoutComponent", | ||
| 1087 | new Object[]{child, comptable.get(child)}, out); | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | } | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | // Swing | ||
| 1094 | |||
| 1095 | // JFrame (If we do this for Window instead of JFrame, the setVisible call | ||
| 1096 | // will be issued before we have added all the children to the JFrame and | ||
| 1097 | // will appear blank). | ||
| 1098 | class javax_swing_JFrame_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1099 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1100 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1101 | java.awt.Window oldC = (java.awt.Window)oldInstance; | ||
| 1102 | java.awt.Window newC = (java.awt.Window)newInstance; | ||
| 1103 | boolean oldV = oldC.isVisible(); | ||
| 1104 | boolean newV = newC.isVisible(); | ||
| 1105 | if (newV != oldV) { | ||
| 1106 | // false means: don't execute this statement at write time. | ||
| 1107 | boolean executeStatements = out.executeStatements; | ||
| 1108 | out.executeStatements = false; | ||
| 1109 | invokeStatement(oldInstance, "setVisible", new Object[]{Boolean.valueOf(oldV)}, out); | ||
| 1110 | out.executeStatements = executeStatements; | ||
| 1111 | } | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | // Models | ||
| 1116 | |||
| 1117 | // DefaultListModel | ||
| 1118 | class javax_swing_DefaultListModel_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1119 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1120 | // Note, the "size" property will be set here. | ||
| 1121 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1122 | javax.swing.DefaultListModel m = (javax.swing.DefaultListModel)oldInstance; | ||
| 1123 | javax.swing.DefaultListModel n = (javax.swing.DefaultListModel)newInstance; | ||
| 1124 | for (int i = n.getSize(); i < m.getSize(); i++) { | ||
| 1125 | invokeStatement(oldInstance, "add", // Can also use "addElement". | ||
| 1126 | new Object[]{m.getElementAt(i)}, out); | ||
| 1127 | } | ||
| 1128 | } | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | // DefaultComboBoxModel | ||
| 1132 | class javax_swing_DefaultComboBoxModel_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1133 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1134 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1135 | javax.swing.DefaultComboBoxModel m = (javax.swing.DefaultComboBoxModel)oldInstance; | ||
| 1136 | for (int i = 0; i < m.getSize(); i++) { | ||
| 1137 | invokeStatement(oldInstance, "addElement", new Object[]{m.getElementAt(i)}, out); | ||
| 1138 | } | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | |||
| 1143 | // DefaultMutableTreeNode | ||
| 1144 | class javax_swing_tree_DefaultMutableTreeNode_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1145 | protected void initialize(Class<?> type, Object oldInstance, Object | ||
| 1146 | newInstance, Encoder out) { | ||
| 1147 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1148 | javax.swing.tree.DefaultMutableTreeNode m = | ||
| 1149 | (javax.swing.tree.DefaultMutableTreeNode)oldInstance; | ||
| 1150 | javax.swing.tree.DefaultMutableTreeNode n = | ||
| 1151 | (javax.swing.tree.DefaultMutableTreeNode)newInstance; | ||
| 1152 | for (int i = n.getChildCount(); i < m.getChildCount(); i++) { | ||
| 1153 | invokeStatement(oldInstance, "add", new | ||
| 1154 | Object[]{m.getChildAt(i)}, out); | ||
| 1155 | } | ||
| 1156 | } | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | // ToolTipManager | ||
| 1160 | class javax_swing_ToolTipManager_PersistenceDelegate extends PersistenceDelegate { | ||
| 1161 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 1162 | return new Expression(oldInstance, javax.swing.ToolTipManager.class, | ||
| 1163 | "sharedInstance", new Object[]{}); | ||
| 1164 | } | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | // JTabbedPane | ||
| 1168 | class javax_swing_JTabbedPane_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1169 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1170 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1171 | javax.swing.JTabbedPane p = (javax.swing.JTabbedPane)oldInstance; | ||
| 1172 | for (int i = 0; i < p.getTabCount(); i++) { | ||
| 1173 | invokeStatement(oldInstance, "addTab", | ||
| 1174 | new Object[]{ | ||
| 1175 | p.getTitleAt(i), | ||
| 1176 | p.getIconAt(i), | ||
| 1177 | p.getComponentAt(i)}, out); | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | // Box | ||
| 1183 | class javax_swing_Box_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1184 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 1185 | return super.mutatesTo(oldInstance, newInstance) && getAxis(oldInstance).equals(getAxis(newInstance)); | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 1189 | return new Expression(oldInstance, oldInstance.getClass(), "new", new Object[] {getAxis(oldInstance)}); | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | private Integer getAxis(Object object) { | ||
| 1193 | Box box = (Box) object; | ||
| 1194 | return (Integer) MetaData.getPrivateFieldValue(box.getLayout(), "javax.swing.BoxLayout.axis"); | ||
| 1195 | } | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | // JMenu | ||
| 1199 | // Note that we do not need to state the initialiser for | ||
| 1200 | // JMenuItems since the getComponents() method defined in | ||
| 1201 | // Container will return all of the sub menu items that | ||
| 1202 | // need to be added to the menu item. | ||
| 1203 | // Not so for JMenu apparently. | ||
| 1204 | class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1205 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1206 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1207 | javax.swing.JMenu m = (javax.swing.JMenu)oldInstance; | ||
| 1208 | java.awt.Component[] c = m.getMenuComponents(); | ||
| 1209 | for (int i = 0; i < c.length; i++) { | ||
| 1210 | invokeStatement(oldInstance, "add", new Object[]{c[i]}, out); | ||
| 1211 | } | ||
| 1212 | } | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | /** | ||
| 1216 | * The persistence delegate for {@link MatteBorder}. | ||
| 1217 | * It is impossible to use {@link DefaultPersistenceDelegate} | ||
| 1218 | * because this class does not have writable properties. | ||
| 1219 | * | ||
| 1220 | * @author Sergey A. Malenkov | ||
| 1221 | */ | ||
| 1222 | final class javax_swing_border_MatteBorder_PersistenceDelegate extends PersistenceDelegate { | ||
| 1223 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 1224 | MatteBorder border = (MatteBorder) oldInstance; | ||
| 1225 | Insets insets = border.getBorderInsets(); | ||
| 1226 | Object object = border.getTileIcon(); | ||
| 1227 | if (object == null) { | ||
| 1228 | object = border.getMatteColor(); | ||
| 1229 | } | ||
| 1230 | Object[] args = new Object[] { | ||
| 1231 | insets.top, | ||
| 1232 | insets.left, | ||
| 1233 | insets.bottom, | ||
| 1234 | insets.right, | ||
| 1235 | object, | ||
| 1236 | }; | ||
| 1237 | return new Expression(border, border.getClass(), "new", args); | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | /* XXX - doens't seem to work. Debug later. | ||
| 1242 | class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate { | ||
| 1243 | protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) { | ||
| 1244 | super.initialize(type, oldInstance, newInstance, out); | ||
| 1245 | javax.swing.JMenu m = (javax.swing.JMenu)oldInstance; | ||
| 1246 | javax.swing.JMenu n = (javax.swing.JMenu)newInstance; | ||
| 1247 | for (int i = n.getItemCount(); i < m.getItemCount(); i++) { | ||
| 1248 | invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out); | ||
| 1249 | } | ||
| 1250 | } | ||
| 1251 | } | ||
| 1252 | */ | ||
| 1253 | |||
| 1254 | /** | ||
| 1255 | * The persistence delegate for {@link PrintColorUIResource}. | ||
| 1256 | * It is impossible to use {@link DefaultPersistenceDelegate} | ||
| 1257 | * because this class has special rule for serialization: | ||
| 1258 | * it should be converted to {@link ColorUIResource}. | ||
| 1259 | * | ||
| 1260 | * @see PrintColorUIResource#writeReplace | ||
| 1261 | * | ||
| 1262 | * @author Sergey A. Malenkov | ||
| 1263 | */ | ||
| 1264 | final class sun_swing_PrintColorUIResource_PersistenceDelegate extends PersistenceDelegate { | ||
| 1265 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 1266 | return oldInstance.equals(newInstance); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | protected Expression instantiate(Object oldInstance, Encoder out) { | ||
| 1270 | Color color = (Color) oldInstance; | ||
| 1271 | Object[] args = new Object[] {color.getRGB()}; | ||
| 1272 | return new Expression(color, ColorUIResource.class, "new", args); | ||
| 1273 | } | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | class MetaData { | ||
| 1277 | private static final Map<String,Field> fields = Collections.synchronizedMap(new WeakHashMap<String, Field>()); | ||
| 1278 | private static Hashtable internalPersistenceDelegates = new Hashtable(); | ||
| 1279 | |||
| 1280 | private static PersistenceDelegate nullPersistenceDelegate = new NullPersistenceDelegate(); | ||
| 1281 | private static PersistenceDelegate enumPersistenceDelegate = new EnumPersistenceDelegate(); | ||
| 1282 | private static PersistenceDelegate primitivePersistenceDelegate = new PrimitivePersistenceDelegate(); | ||
| 1283 | private static PersistenceDelegate defaultPersistenceDelegate = new DefaultPersistenceDelegate(); | ||
| 1284 | private static PersistenceDelegate arrayPersistenceDelegate; | ||
| 1285 | private static PersistenceDelegate proxyPersistenceDelegate; | ||
| 1286 | |||
| 1287 | static { | ||
| 1288 | |||
| 1289 | internalPersistenceDelegates.put("java.net.URI", | ||
| 1290 | new PrimitivePersistenceDelegate()); | ||
| 1291 | |||
| 1292 | // it is possible because MatteBorder is assignable from MatteBorderUIResource | ||
| 1293 | internalPersistenceDelegates.put("javax.swing.plaf.BorderUIResource$MatteBorderUIResource", | ||
| 1294 | new javax_swing_border_MatteBorder_PersistenceDelegate()); | ||
| 1295 | |||
| 1296 | // it is possible because FontUIResource is supported by java_awt_Font_PersistenceDelegate | ||
| 1297 | internalPersistenceDelegates.put("javax.swing.plaf.FontUIResource", | ||
| 1298 | new java_awt_Font_PersistenceDelegate()); | ||
| 1299 | |||
| 1300 | // it is possible because KeyStroke is supported by java_awt_AWTKeyStroke_PersistenceDelegate | ||
| 1301 | internalPersistenceDelegates.put("javax.swing.KeyStroke", | ||
| 1302 | new java_awt_AWTKeyStroke_PersistenceDelegate()); | ||
| 1303 | |||
| 1304 | internalPersistenceDelegates.put("java.sql.Date", new java_util_Date_PersistenceDelegate()); | ||
| 1305 | internalPersistenceDelegates.put("java.sql.Time", new java_util_Date_PersistenceDelegate()); | ||
| 1306 | |||
| 1307 | internalPersistenceDelegates.put("java.util.JumboEnumSet", new java_util_EnumSet_PersistenceDelegate()); | ||
| 1308 | internalPersistenceDelegates.put("java.util.RegularEnumSet", new java_util_EnumSet_PersistenceDelegate()); | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | public synchronized static PersistenceDelegate getPersistenceDelegate(Class type) { | ||
| 1312 | if (type == null) { | ||
| 1313 | return nullPersistenceDelegate; | ||
| 1314 | } | ||
| 1315 | if (Enum.class.isAssignableFrom(type)) { | ||
| 1316 | return enumPersistenceDelegate; | ||
| 1317 | } | ||
| 1318 | if (ReflectionUtils.isPrimitive(type)) { | ||
| 1319 | return primitivePersistenceDelegate; | ||
| 1320 | } | ||
| 1321 | // The persistence delegate for arrays is non-trivial; instantiate it lazily. | ||
| 1322 | if (type.isArray()) { | ||
| 1323 | if (arrayPersistenceDelegate == null) { | ||
| 1324 | arrayPersistenceDelegate = new ArrayPersistenceDelegate(); | ||
| 1325 | } | ||
| 1326 | return arrayPersistenceDelegate; | ||
| 1327 | } | ||
| 1328 | // Handle proxies lazily for backward compatibility with 1.2. | ||
| 1329 | try { | ||
| 1330 | if (java.lang.reflect.Proxy.isProxyClass(type)) { | ||
| 1331 | if (proxyPersistenceDelegate == null) { | ||
| 1332 | proxyPersistenceDelegate = new ProxyPersistenceDelegate(); | ||
| 1333 | } | ||
| 1334 | return proxyPersistenceDelegate; | ||
| 1335 | } | ||
| 1336 | } | ||
| 1337 | catch(Exception e) {} | ||
| 1338 | // else if (type.getDeclaringClass() != null) { | ||
| 1339 | // return new DefaultPersistenceDelegate(new String[]{"this$0"}); | ||
| 1340 | // } | ||
| 1341 | |||
| 1342 | String typeName = type.getName(); | ||
| 1343 | PersistenceDelegate pd = (PersistenceDelegate)getBeanAttribute(type, "persistenceDelegate"); | ||
| 1344 | if (pd == null) { | ||
| 1345 | pd = (PersistenceDelegate)internalPersistenceDelegates.get(typeName); | ||
| 1346 | if (pd != null) { | ||
| 1347 | return pd; | ||
| 1348 | } | ||
| 1349 | internalPersistenceDelegates.put(typeName, defaultPersistenceDelegate); | ||
| 1350 | try { | ||
| 1351 | String name = type.getName(); | ||
| 1352 | Class c = Class.forName("java.beans." + name.replace('.', '_') | ||
| 1353 | + "_PersistenceDelegate"); | ||
| 1354 | pd = (PersistenceDelegate)c.newInstance(); | ||
| 1355 | internalPersistenceDelegates.put(typeName, pd); | ||
| 1356 | } | ||
| 1357 | catch (ClassNotFoundException e) { | ||
| 1358 | String[] properties = getConstructorProperties(type); | ||
| 1359 | if (properties != null) { | ||
| 1360 | pd = new DefaultPersistenceDelegate(properties); | ||
| 1361 | internalPersistenceDelegates.put(typeName, pd); | ||
| 1362 | } | ||
| 1363 | } | ||
| 1364 | catch (Exception e) { | ||
| 1365 | System.err.println("Internal error: " + e); | ||
| 1366 | } | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | return (pd != null) ? pd : defaultPersistenceDelegate; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | private static String[] getConstructorProperties(Class type) { | ||
| 1373 | String[] names = null; | ||
| 1374 | int length = 0; | ||
| 1375 | for (Constructor<?> constructor : type.getConstructors()) { | ||
| 1376 | String[] value = getAnnotationValue(constructor); | ||
| 1377 | if ((value != null) && (length < value.length) && isValid(constructor, value)) { | ||
| 1378 | names = value; | ||
| 1379 | length = value.length; | ||
| 1380 | } | ||
| 1381 | } | ||
| 1382 | return names; | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | private static String[] getAnnotationValue(Constructor<?> constructor) { | ||
| 1386 | ConstructorProperties annotation = constructor.getAnnotation(ConstructorProperties.class); | ||
| 1387 | return (annotation != null) | ||
| 1388 | ? annotation.value() | ||
| 1389 | : null; | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | private static boolean isValid(Constructor<?> constructor, String[] names) { | ||
| 1393 | Class[] parameters = constructor.getParameterTypes(); | ||
| 1394 | if (names.length != parameters.length) { | ||
| 1395 | return false; | ||
| 1396 | } | ||
| 1397 | for (String name : names) { | ||
| 1398 | if (name == null) { | ||
| 1399 | return false; | ||
| 1400 | } | ||
| 1401 | } | ||
| 1402 | return true; | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | private static Object getBeanAttribute(Class type, String attribute) { | ||
| 1406 | try { | ||
| 1407 | return Introspector.getBeanInfo(type).getBeanDescriptor().getValue(attribute); | ||
| 1408 | } catch (IntrospectionException exception) { | ||
| 1409 | return null; | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | static Object getPrivateFieldValue(Object instance, String name) { | ||
| 1414 | Field field = fields.get(name); | ||
| 1415 | if (field == null) { | ||
| 1416 | int index = name.lastIndexOf('.'); | ||
| 1417 | final String className = name.substring(0, index); | ||
| 1418 | final String fieldName = name.substring(1 + index); | ||
| 1419 | field = AccessController.doPrivileged(new PrivilegedAction<Field>() { | ||
| 1420 | public Field run() { | ||
| 1421 | try { | ||
| 1422 | Field field = Class.forName(className).getDeclaredField(fieldName); | ||
| 1423 | field.setAccessible(true); | ||
| 1424 | return field; | ||
| 1425 | } | ||
| 1426 | catch (ClassNotFoundException exception) { | ||
| 1427 | throw new IllegalStateException("Could not find class", exception); | ||
| 1428 | } | ||
| 1429 | catch (NoSuchFieldException exception) { | ||
| 1430 | throw new IllegalStateException("Could not find field", exception); | ||
| 1431 | } | ||
| 1432 | } | ||
| 1433 | }); | ||
| 1434 | fields.put(name, field); | ||
| 1435 | } | ||
| 1436 | try { | ||
| 1437 | return field.get(instance); | ||
| 1438 | } | ||
| 1439 | catch (IllegalAccessException exception) { | ||
| 1440 | throw new IllegalStateException("Could not get value of the field", exception); | ||
| 1441 | } | ||
| 1442 | } | ||
| 1443 | } | ||
Binary file not shown.
| @@ -0,0 +1,228 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.lang.ref.Reference; | ||
| 29 | import java.lang.ref.WeakReference; | ||
| 30 | import java.lang.reflect.Method; | ||
| 31 | import java.util.List; | ||
| 32 | import java.util.ArrayList; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * A MethodDescriptor describes a particular method that a Java Bean | ||
| 36 | * supports for external access from other components. | ||
| 37 | */ | ||
| 38 | |||
| 39 | public class MethodDescriptor extends FeatureDescriptor { | ||
| 40 | |||
| 41 | private Reference<Method> methodRef; | ||
| 42 | |||
| 43 | private String[] paramNames; | ||
| 44 | |||
| 45 | private List params; | ||
| 46 | |||
| 47 | private ParameterDescriptor parameterDescriptors[]; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Constructs a <code>MethodDescriptor</code> from a | ||
| 51 | * <code>Method</code>. | ||
| 52 | * | ||
| 53 | * @param method The low-level method information. | ||
| 54 | */ | ||
| 55 | public MethodDescriptor(Method method) { | ||
| 56 | this(method, null); | ||
| 57 | } | ||
| 58 | |||
| 59 | |||
| 60 | /** | ||
| 61 | * Constructs a <code>MethodDescriptor</code> from a | ||
| 62 | * <code>Method</code> providing descriptive information for each | ||
| 63 | * of the method's parameters. | ||
| 64 | * | ||
| 65 | * @param method The low-level method information. | ||
| 66 | * @param parameterDescriptors Descriptive information for each of the | ||
| 67 | * method's parameters. | ||
| 68 | */ | ||
| 69 | public MethodDescriptor(Method method, | ||
| 70 | ParameterDescriptor parameterDescriptors[]) { | ||
| 71 | setName(method.getName()); | ||
| 72 | setMethod(method); | ||
| 73 | this.parameterDescriptors = parameterDescriptors; | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Gets the method that this MethodDescriptor encapsualtes. | ||
| 78 | * | ||
| 79 | * @return The low-level description of the method | ||
| 80 | */ | ||
| 81 | public synchronized Method getMethod() { | ||
| 82 | Method method = getMethod0(); | ||
| 83 | if (method == null) { | ||
| 84 | Class cls = getClass0(); | ||
| 85 | String name = getName(); | ||
| 86 | if ((cls != null) && (name != null)) { | ||
| 87 | Class[] params = getParams(); | ||
| 88 | if (params == null) { | ||
| 89 | for (int i = 0; i < 3; i++) { | ||
| 90 | // Find methods for up to 2 params. We are guessing here. | ||
| 91 | // This block should never execute unless the classloader | ||
| 92 | // that loaded the argument classes disappears. | ||
| 93 | method = Introspector.findMethod(cls, name, i, null); | ||
| 94 | if (method != null) { | ||
| 95 | break; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } else { | ||
| 99 | method = Introspector.findMethod(cls, name, params.length, params); | ||
| 100 | } | ||
| 101 | setMethod(method); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | return method; | ||
| 105 | } | ||
| 106 | |||
| 107 | private synchronized void setMethod(Method method) { | ||
| 108 | if (method == null) { | ||
| 109 | return; | ||
| 110 | } | ||
| 111 | if (getClass0() == null) { | ||
| 112 | setClass0(method.getDeclaringClass()); | ||
| 113 | } | ||
| 114 | setParams(getParameterTypes(getClass0(), method)); | ||
| 115 | this.methodRef = getSoftReference(method); | ||
| 116 | } | ||
| 117 | |||
| 118 | private Method getMethod0() { | ||
| 119 | return (this.methodRef != null) | ||
| 120 | ? this.methodRef.get() | ||
| 121 | : null; | ||
| 122 | } | ||
| 123 | |||
| 124 | private synchronized void setParams(Class[] param) { | ||
| 125 | if (param == null) { | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | paramNames = new String[param.length]; | ||
| 129 | params = new ArrayList(param.length); | ||
| 130 | for (int i = 0; i < param.length; i++) { | ||
| 131 | paramNames[i] = param[i].getName(); | ||
| 132 | params.add(new WeakReference(param[i])); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | // pp getParamNames used as an optimization to avoid method.getParameterTypes. | ||
| 137 | String[] getParamNames() { | ||
| 138 | return paramNames; | ||
| 139 | } | ||
| 140 | |||
| 141 | private synchronized Class[] getParams() { | ||
| 142 | Class[] clss = new Class[params.size()]; | ||
| 143 | |||
| 144 | for (int i = 0; i < params.size(); i++) { | ||
| 145 | Reference ref = (Reference)params.get(i); | ||
| 146 | Class cls = (Class)ref.get(); | ||
| 147 | if (cls == null) { | ||
| 148 | return null; | ||
| 149 | } else { | ||
| 150 | clss[i] = cls; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | return clss; | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Gets the ParameterDescriptor for each of this MethodDescriptor's | ||
| 158 | * method's parameters. | ||
| 159 | * | ||
| 160 | * @return The locale-independent names of the parameters. May return | ||
| 161 | * a null array if the parameter names aren't known. | ||
| 162 | */ | ||
| 163 | public ParameterDescriptor[] getParameterDescriptors() { | ||
| 164 | return parameterDescriptors; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Package-private constructor | ||
| 169 | * Merge two method descriptors. Where they conflict, give the | ||
| 170 | * second argument (y) priority over the first argument (x). | ||
| 171 | * @param x The first (lower priority) MethodDescriptor | ||
| 172 | * @param y The second (higher priority) MethodDescriptor | ||
| 173 | */ | ||
| 174 | |||
| 175 | MethodDescriptor(MethodDescriptor x, MethodDescriptor y) { | ||
| 176 | super(x,y); | ||
| 177 | |||
| 178 | methodRef = x.methodRef; | ||
| 179 | if (y.methodRef != null) { | ||
| 180 | methodRef = y.methodRef; | ||
| 181 | } | ||
| 182 | params = x.params; | ||
| 183 | if (y.params != null) { | ||
| 184 | params = y.params; | ||
| 185 | } | ||
| 186 | paramNames = x.paramNames; | ||
| 187 | if (y.paramNames != null) { | ||
| 188 | paramNames = y.paramNames; | ||
| 189 | } | ||
| 190 | |||
| 191 | parameterDescriptors = x.parameterDescriptors; | ||
| 192 | if (y.parameterDescriptors != null) { | ||
| 193 | parameterDescriptors = y.parameterDescriptors; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | /* | ||
| 198 | * Package-private dup constructor | ||
| 199 | * This must isolate the new object from any changes to the old object. | ||
| 200 | */ | ||
| 201 | MethodDescriptor(MethodDescriptor old) { | ||
| 202 | super(old); | ||
| 203 | |||
| 204 | methodRef = old.methodRef; | ||
| 205 | params = old.params; | ||
| 206 | paramNames = old.paramNames; | ||
| 207 | |||
| 208 | if (old.parameterDescriptors != null) { | ||
| 209 | int len = old.parameterDescriptors.length; | ||
| 210 | parameterDescriptors = new ParameterDescriptor[len]; | ||
| 211 | for (int i = 0; i < len ; i++) { | ||
| 212 | parameterDescriptors[i] = new ParameterDescriptor(old.parameterDescriptors[i]); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | void appendTo(StringBuilder sb) { | ||
| 218 | appendTo(sb, "method", this.methodRef); | ||
| 219 | if (this.parameterDescriptors != null) { | ||
| 220 | sb.append("; parameterDescriptors={"); | ||
| 221 | for (ParameterDescriptor pd : this.parameterDescriptors) { | ||
| 222 | sb.append(pd).append(", "); | ||
| 223 | } | ||
| 224 | sb.setLength(sb.length() - 2); | ||
| 225 | sb.append("}"); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
Binary file not shown.
| @@ -0,0 +1,116 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.util.HashMap; | ||
| 28 | import java.util.IdentityHashMap; | ||
| 29 | import java.util.Map; | ||
| 30 | |||
| 31 | import static java.util.Locale.ENGLISH; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * A utility class which generates unique names for object instances. | ||
| 35 | * The name will be a concatenation of the unqualified class name | ||
| 36 | * and an instance number. | ||
| 37 | * <p> | ||
| 38 | * For example, if the first object instance javax.swing.JButton | ||
| 39 | * is passed into <code>instanceName</code> then the returned | ||
| 40 | * string identifier will be "JButton0". | ||
| 41 | * | ||
| 42 | * @author Philip Milne | ||
| 43 | */ | ||
| 44 | class NameGenerator { | ||
| 45 | |||
| 46 | private Map valueToName; | ||
| 47 | private Map nameToCount; | ||
| 48 | |||
| 49 | public NameGenerator() { | ||
| 50 | valueToName = new IdentityHashMap(); | ||
| 51 | nameToCount = new HashMap(); | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Clears the name cache. Should be called to near the end of | ||
| 56 | * the encoding cycle. | ||
| 57 | */ | ||
| 58 | public void clear() { | ||
| 59 | valueToName.clear(); | ||
| 60 | nameToCount.clear(); | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Returns the root name of the class. | ||
| 65 | */ | ||
| 66 | public static String unqualifiedClassName(Class type) { | ||
| 67 | if (type.isArray()) { | ||
| 68 | return unqualifiedClassName(type.getComponentType())+"Array"; | ||
| 69 | } | ||
| 70 | String name = type.getName(); | ||
| 71 | return name.substring(name.lastIndexOf('.')+1); | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Returns a String which capitalizes the first letter of the string. | ||
| 76 | */ | ||
| 77 | public static String capitalize(String name) { | ||
| 78 | if (name == null || name.length() == 0) { | ||
| 79 | return name; | ||
| 80 | } | ||
| 81 | return name.substring(0, 1).toUpperCase(ENGLISH) + name.substring(1); | ||
| 82 | } | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Returns a unique string which identifies the object instance. | ||
| 86 | * Invocations are cached so that if an object has been previously | ||
| 87 | * passed into this method then the same identifier is returned. | ||
| 88 | * | ||
| 89 | * @param instance object used to generate string | ||
| 90 | * @return a unique string representing the object | ||
| 91 | */ | ||
| 92 | public String instanceName(Object instance) { | ||
| 93 | if (instance == null) { | ||
| 94 | return "null"; | ||
| 95 | } | ||
| 96 | if (instance instanceof Class) { | ||
| 97 | return unqualifiedClassName((Class)instance); | ||
| 98 | } | ||
| 99 | else { | ||
| 100 | String result = (String)valueToName.get(instance); | ||
| 101 | if (result != null) { | ||
| 102 | return result; | ||
| 103 | } | ||
| 104 | Class type = instance.getClass(); | ||
| 105 | String className = unqualifiedClassName(type); | ||
| 106 | |||
| 107 | Object size = nameToCount.get(className); | ||
| 108 | int instanceNumber = (size == null) ? 0 : ((Integer)size).intValue() + 1; | ||
| 109 | nameToCount.put(className, new Integer(instanceNumber)); | ||
| 110 | |||
| 111 | result = className + instanceNumber; | ||
| 112 | valueToName.put(instance, result); | ||
| 113 | return result; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,53 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 1997, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * The ParameterDescriptor class allows bean implementors to provide | ||
| 30 | * additional information on each of their parameters, beyond the | ||
| 31 | * low level type information provided by the java.lang.reflect.Method | ||
| 32 | * class. | ||
| 33 | * <p> | ||
| 34 | * Currently all our state comes from the FeatureDescriptor base class. | ||
| 35 | */ | ||
| 36 | |||
| 37 | public class ParameterDescriptor extends FeatureDescriptor { | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Public default constructor. | ||
| 41 | */ | ||
| 42 | public ParameterDescriptor() { | ||
| 43 | } | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Package private dup constructor. | ||
| 47 | * This must isolate the new object from any changes to the old object. | ||
| 48 | */ | ||
| 49 | ParameterDescriptor(ParameterDescriptor old) { | ||
| 50 | super(old); | ||
| 51 | } | ||
| 52 | |||
| 53 | } | ||
Binary file not shown.
| @@ -0,0 +1,214 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * The PersistenceDelegate class takes the responsibility | ||
| 29 | * for expressing the state of an instance of a given class | ||
| 30 | * in terms of the methods in the class's public API. Instead | ||
| 31 | * of associating the responsibility of persistence with | ||
| 32 | * the class itself as is done, for example, by the | ||
| 33 | * <code>readObject</code> and <code>writeObject</code> | ||
| 34 | * methods used by the <code>ObjectOutputStream</code>, streams like | ||
| 35 | * the <code>XMLEncoder</code> which | ||
| 36 | * use this delegation model can have their behavior controlled | ||
| 37 | * independently of the classes themselves. Normally, the class | ||
| 38 | * is the best place to put such information and conventions | ||
| 39 | * can easily be expressed in this delegation scheme to do just that. | ||
| 40 | * Sometimes however, it is the case that a minor problem | ||
| 41 | * in a single class prevents an entire object graph from | ||
| 42 | * being written and this can leave the application | ||
| 43 | * developer with no recourse but to attempt to shadow | ||
| 44 | * the problematic classes locally or use alternative | ||
| 45 | * persistence techniques. In situations like these, the | ||
| 46 | * delegation model gives a relatively clean mechanism for | ||
| 47 | * the application developer to intervene in all parts of the | ||
| 48 | * serialization process without requiring that modifications | ||
| 49 | * be made to the implementation of classes which are not part | ||
| 50 | * of the application itself. | ||
| 51 | * <p> | ||
| 52 | * In addition to using a delegation model, this persistence | ||
| 53 | * scheme differs from traditional serialization schemes | ||
| 54 | * in requiring an analog of the <code>writeObject</code> | ||
| 55 | * method without a corresponding <code>readObject</code> | ||
| 56 | * method. The <code>writeObject</code> analog encodes each | ||
| 57 | * instance in terms of its public API and there is no need to | ||
| 58 | * define a <code>readObject</code> analog | ||
| 59 | * since the procedure for reading the serialized form | ||
| 60 | * is defined by the semantics of method invocation as laid | ||
| 61 | * out in the Java Language Specification. | ||
| 62 | * Breaking the dependency between <code>writeObject</code> | ||
| 63 | * and <code>readObject</code> implementations, which may | ||
| 64 | * change from version to version, is the key factor | ||
| 65 | * in making the archives produced by this technique immune | ||
| 66 | * to changes in the private implementations of the classes | ||
| 67 | * to which they refer. | ||
| 68 | * <p> | ||
| 69 | * A persistence delegate, may take control of all | ||
| 70 | * aspects of the persistence of an object including: | ||
| 71 | * <ul> | ||
| 72 | * <li> | ||
| 73 | * Deciding whether or not an instance can be mutated | ||
| 74 | * into another instance of the same class. | ||
| 75 | * <li> | ||
| 76 | * Instantiating the object, either by calling a | ||
| 77 | * public constructor or a public factory method. | ||
| 78 | * <li> | ||
| 79 | * Performing the initialization of the object. | ||
| 80 | * </ul> | ||
| 81 | * @see XMLEncoder | ||
| 82 | * | ||
| 83 | * @since 1.4 | ||
| 84 | * | ||
| 85 | * @author Philip Milne | ||
| 86 | */ | ||
| 87 | |||
| 88 | public abstract class PersistenceDelegate { | ||
| 89 | |||
| 90 | /** | ||
| 91 | * The <code>writeObject</code> is a single entry point to the persistence | ||
| 92 | * and is used by a <code>Encoder</code> in the traditional | ||
| 93 | * mode of delegation. Although this method is not final, | ||
| 94 | * it should not need to be subclassed under normal circumstances. | ||
| 95 | * <p> | ||
| 96 | * This implementation first checks to see if the stream | ||
| 97 | * has already encountered this object. Next the | ||
| 98 | * <code>mutatesTo</code> method is called to see if | ||
| 99 | * that candidate returned from the stream can | ||
| 100 | * be mutated into an accurate copy of <code>oldInstance</code>. | ||
| 101 | * If it can, the <code>initialize</code> method is called to | ||
| 102 | * perform the initialization. If not, the candidate is removed | ||
| 103 | * from the stream, and the <code>instantiate</code> method | ||
| 104 | * is called to create a new candidate for this object. | ||
| 105 | * | ||
| 106 | * @param oldInstance The instance that will be created by this expression. | ||
| 107 | * @param out The stream to which this expression will be written. | ||
| 108 | * | ||
| 109 | * @throws NullPointerException if {@code out} is {@code null} | ||
| 110 | */ | ||
| 111 | public void writeObject(Object oldInstance, Encoder out) { | ||
| 112 | Object newInstance = out.get(oldInstance); | ||
| 113 | if (!mutatesTo(oldInstance, newInstance)) { | ||
| 114 | out.remove(oldInstance); | ||
| 115 | out.writeExpression(instantiate(oldInstance, out)); | ||
| 116 | } | ||
| 117 | else { | ||
| 118 | initialize(oldInstance.getClass(), oldInstance, newInstance, out); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Returns true if an <em>equivalent</em> copy of <code>oldInstance</code> may be | ||
| 124 | * created by applying a series of statements to <code>newInstance</code>. | ||
| 125 | * In the specification of this method, we mean by equivalent that the modified instance | ||
| 126 | * is indistinguishable from <code>oldInstance</code> in the behavior | ||
| 127 | * of the relevant methods in its public API. [Note: we use the | ||
| 128 | * phrase <em>relevant</em> methods rather than <em>all</em> methods | ||
| 129 | * here only because, to be strictly correct, methods like <code>hashCode</code> | ||
| 130 | * and <code>toString</code> prevent most classes from producing truly | ||
| 131 | * indistinguishable copies of their instances]. | ||
| 132 | * <p> | ||
| 133 | * The default behavior returns <code>true</code> | ||
| 134 | * if the classes of the two instances are the same. | ||
| 135 | * | ||
| 136 | * @param oldInstance The instance to be copied. | ||
| 137 | * @param newInstance The instance that is to be modified. | ||
| 138 | * @return True if an equivalent copy of <code>newInstance</code> may be | ||
| 139 | * created by applying a series of mutations to <code>oldInstance</code>. | ||
| 140 | */ | ||
| 141 | protected boolean mutatesTo(Object oldInstance, Object newInstance) { | ||
| 142 | return (newInstance != null && oldInstance != null && | ||
| 143 | oldInstance.getClass() == newInstance.getClass()); | ||
| 144 | } | ||
| 145 | |||
| 146 | /** | ||
| 147 | * Returns an expression whose value is <code>oldInstance</code>. | ||
| 148 | * This method is used to characterize the constructor | ||
| 149 | * or factory method that should be used to create the given object. | ||
| 150 | * For example, the <code>instantiate</code> method of the persistence | ||
| 151 | * delegate for the <code>Field</code> class could be defined as follows: | ||
| 152 | * <pre> | ||
| 153 | * Field f = (Field)oldInstance; | ||
| 154 | * return new Expression(f, f.getDeclaringClass(), "getField", new Object[]{f.getName()}); | ||
| 155 | * </pre> | ||
| 156 | * Note that we declare the value of the returned expression so that | ||
| 157 | * the value of the expression (as returned by <code>getValue</code>) | ||
| 158 | * will be identical to <code>oldInstance</code>. | ||
| 159 | * | ||
| 160 | * @param oldInstance The instance that will be created by this expression. | ||
| 161 | * @param out The stream to which this expression will be written. | ||
| 162 | * @return An expression whose value is <code>oldInstance</code>. | ||
| 163 | * | ||
| 164 | * @throws NullPointerException if {@code out} is {@code null} | ||
| 165 | */ | ||
| 166 | protected abstract Expression instantiate(Object oldInstance, Encoder out); | ||
| 167 | |||
| 168 | /** | ||
| 169 | * Produce a series of statements with side effects on <code>newInstance</code> | ||
| 170 | * so that the new instance becomes <em>equivalent</em> to <code>oldInstance</code>. | ||
| 171 | * In the specification of this method, we mean by equivalent that, after the method | ||
| 172 | * returns, the modified instance is indistinguishable from | ||
| 173 | * <code>newInstance</code> in the behavior of all methods in its | ||
| 174 | * public API. | ||
| 175 | * <p> | ||
| 176 | * The implementation typically achieves this goal by producing a series of | ||
| 177 | * "what happened" statements involving the <code>oldInstance</code> | ||
| 178 | * and its publicly available state. These statements are sent | ||
| 179 | * to the output stream using its <code>writeExpression</code> | ||
| 180 | * method which returns an expression involving elements in | ||
| 181 | * a cloned environment simulating the state of an input stream during | ||
| 182 | * reading. Each statement returned will have had all instances | ||
| 183 | * the old environment replaced with objects which exist in the new | ||
| 184 | * one. In particular, references to the target of these statements, | ||
| 185 | * which start out as references to <code>oldInstance</code> are returned | ||
| 186 | * as references to the <code>newInstance</code> instead. | ||
| 187 | * Executing these statements effects an incremental | ||
| 188 | * alignment of the state of the two objects as a series of | ||
| 189 | * modifications to the objects in the new environment. | ||
| 190 | * By the time the initialize method returns it should be impossible | ||
| 191 | * to tell the two instances apart by using their public APIs. | ||
| 192 | * Most importantly, the sequence of steps that were used to make | ||
| 193 | * these objects appear equivalent will have been recorded | ||
| 194 | * by the output stream and will form the actual output when | ||
| 195 | * the stream is flushed. | ||
| 196 | * <p> | ||
| 197 | * The default implementation, calls the <code>initialize</code> | ||
| 198 | * method of the type's superclass. | ||
| 199 | * | ||
| 200 | * @param oldInstance The instance to be copied. | ||
| 201 | * @param newInstance The instance that is to be modified. | ||
| 202 | * @param out The stream to which any initialization statements should be written. | ||
| 203 | * | ||
| 204 | * @throws NullPointerException if {@code out} is {@code null} | ||
| 205 | */ | ||
| 206 | protected void initialize(Class<?> type, | ||
| 207 | Object oldInstance, Object newInstance, | ||
| 208 | Encoder out) | ||
| 209 | { | ||
| 210 | Class superType = type.getSuperclass(); | ||
| 211 | PersistenceDelegate info = out.getPersistenceDelegate(superType); | ||
| 212 | info.initialize(superType, oldInstance, newInstance, out); | ||
| 213 | } | ||
| 214 | } | ||
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,164 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A "PropertyChange" event gets delivered whenever a bean changes a "bound" | ||
| 30 | * or "constrained" property. A PropertyChangeEvent object is sent as an | ||
| 31 | * argument to the PropertyChangeListener and VetoableChangeListener methods. | ||
| 32 | * <P> | ||
| 33 | * Normally PropertyChangeEvents are accompanied by the name and the old | ||
| 34 | * and new value of the changed property. If the new value is a primitive | ||
| 35 | * type (such as int or boolean) it must be wrapped as the | ||
| 36 | * corresponding java.lang.* Object type (such as Integer or Boolean). | ||
| 37 | * <P> | ||
| 38 | * Null values may be provided for the old and the new values if their | ||
| 39 | * true values are not known. | ||
| 40 | * <P> | ||
| 41 | * An event source may send a null object as the name to indicate that an | ||
| 42 | * arbitrary set of if its properties have changed. In this case the | ||
| 43 | * old and new values should also be null. | ||
| 44 | */ | ||
| 45 | |||
| 46 | public class PropertyChangeEvent extends java.util.EventObject { | ||
| 47 | private static final long serialVersionUID = 7042693688939648123L; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Constructs a new <code>PropertyChangeEvent</code>. | ||
| 51 | * | ||
| 52 | * @param source The bean that fired the event. | ||
| 53 | * @param propertyName The programmatic name of the property | ||
| 54 | * that was changed. | ||
| 55 | * @param oldValue The old value of the property. | ||
| 56 | * @param newValue The new value of the property. | ||
| 57 | */ | ||
| 58 | public PropertyChangeEvent(Object source, String propertyName, | ||
| 59 | Object oldValue, Object newValue) { | ||
| 60 | super(source); | ||
| 61 | this.propertyName = propertyName; | ||
| 62 | this.newValue = newValue; | ||
| 63 | this.oldValue = oldValue; | ||
| 64 | } | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Gets the programmatic name of the property that was changed. | ||
| 68 | * | ||
| 69 | * @return The programmatic name of the property that was changed. | ||
| 70 | * May be null if multiple properties have changed. | ||
| 71 | */ | ||
| 72 | public String getPropertyName() { | ||
| 73 | return propertyName; | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Gets the new value for the property, expressed as an Object. | ||
| 78 | * | ||
| 79 | * @return The new value for the property, expressed as an Object. | ||
| 80 | * May be null if multiple properties have changed. | ||
| 81 | */ | ||
| 82 | public Object getNewValue() { | ||
| 83 | return newValue; | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Gets the old value for the property, expressed as an Object. | ||
| 88 | * | ||
| 89 | * @return The old value for the property, expressed as an Object. | ||
| 90 | * May be null if multiple properties have changed. | ||
| 91 | */ | ||
| 92 | public Object getOldValue() { | ||
| 93 | return oldValue; | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Sets the propagationId object for the event. | ||
| 98 | * | ||
| 99 | * @param propagationId The propagationId object for the event. | ||
| 100 | */ | ||
| 101 | public void setPropagationId(Object propagationId) { | ||
| 102 | this.propagationId = propagationId; | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * The "propagationId" field is reserved for future use. In Beans 1.0 | ||
| 107 | * the sole requirement is that if a listener catches a PropertyChangeEvent | ||
| 108 | * and then fires a PropertyChangeEvent of its own, then it should | ||
| 109 | * make sure that it propagates the propagationId field from its | ||
| 110 | * incoming event to its outgoing event. | ||
| 111 | * | ||
| 112 | * @return the propagationId object associated with a bound/constrained | ||
| 113 | * property update. | ||
| 114 | */ | ||
| 115 | public Object getPropagationId() { | ||
| 116 | return propagationId; | ||
| 117 | } | ||
| 118 | |||
| 119 | /** | ||
| 120 | * name of the property that changed. May be null, if not known. | ||
| 121 | * @serial | ||
| 122 | */ | ||
| 123 | private String propertyName; | ||
| 124 | |||
| 125 | /** | ||
| 126 | * New value for property. May be null if not known. | ||
| 127 | * @serial | ||
| 128 | */ | ||
| 129 | private Object newValue; | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Previous value for property. May be null if not known. | ||
| 133 | * @serial | ||
| 134 | */ | ||
| 135 | private Object oldValue; | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Propagation ID. May be null. | ||
| 139 | * @serial | ||
| 140 | * @see #getPropagationId | ||
| 141 | */ | ||
| 142 | private Object propagationId; | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Returns a string representation of the object. | ||
| 146 | * | ||
| 147 | * @return a string representation of the object | ||
| 148 | * | ||
| 149 | * @since 1.7 | ||
| 150 | */ | ||
| 151 | public String toString() { | ||
| 152 | StringBuilder sb = new StringBuilder(getClass().getName()); | ||
| 153 | sb.append("[propertyName=").append(getPropertyName()); | ||
| 154 | appendTo(sb); | ||
| 155 | sb.append("; oldValue=").append(getOldValue()); | ||
| 156 | sb.append("; newValue=").append(getNewValue()); | ||
| 157 | sb.append("; propagationId=").append(getPropagationId()); | ||
| 158 | sb.append("; source=").append(getSource()); | ||
| 159 | return sb.append("]").toString(); | ||
| 160 | } | ||
| 161 | |||
| 162 | void appendTo(StringBuilder sb) { | ||
| 163 | } | ||
| 164 | } | ||
Binary file not shown.
| @@ -0,0 +1,44 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A "PropertyChange" event gets fired whenever a bean changes a "bound" | ||
| 30 | * property. You can register a PropertyChangeListener with a source | ||
| 31 | * bean so as to be notified of any bound property updates. | ||
| 32 | */ | ||
| 33 | |||
| 34 | public interface PropertyChangeListener extends java.util.EventListener { | ||
| 35 | |||
| 36 | /** | ||
| 37 | * This method gets called when a bound property is changed. | ||
| 38 | * @param evt A PropertyChangeEvent object describing the event source | ||
| 39 | * and the property that has changed. | ||
| 40 | */ | ||
| 41 | |||
| 42 | void propertyChange(PropertyChangeEvent evt); | ||
| 43 | |||
| 44 | } | ||
Binary file not shown.
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.util.EventListenerProxy; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A class which extends the {@code EventListenerProxy} | ||
| 32 | * specifically for adding a {@code PropertyChangeListener} | ||
| 33 | * with a "bound" property. | ||
| 34 | * Instances of this class can be added | ||
| 35 | * as {@code PropertyChangeListener}s to a bean | ||
| 36 | * which supports firing property change events. | ||
| 37 | * <p> | ||
| 38 | * If the object has a {@code getPropertyChangeListeners} method | ||
| 39 | * then the array returned could be a mixture of {@code PropertyChangeListener} | ||
| 40 | * and {@code PropertyChangeListenerProxy} objects. | ||
| 41 | * | ||
| 42 | * @see java.util.EventListenerProxy | ||
| 43 | * @see PropertyChangeSupport#getPropertyChangeListeners | ||
| 44 | * @since 1.4 | ||
| 45 | */ | ||
| 46 | public class PropertyChangeListenerProxy | ||
| 47 | extends EventListenerProxy<PropertyChangeListener> | ||
| 48 | implements PropertyChangeListener { | ||
| 49 | |||
| 50 | private final String propertyName; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Constructor which binds the {@code PropertyChangeListener} | ||
| 54 | * to a specific property. | ||
| 55 | * | ||
| 56 | * @param propertyName the name of the property to listen on | ||
| 57 | * @param listener the listener object | ||
| 58 | */ | ||
| 59 | public PropertyChangeListenerProxy(String propertyName, PropertyChangeListener listener) { | ||
| 60 | super(listener); | ||
| 61 | this.propertyName = propertyName; | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Forwards the property change event to the listener delegate. | ||
| 66 | * | ||
| 67 | * @param event the property change event | ||
| 68 | */ | ||
| 69 | public void propertyChange(PropertyChangeEvent event) { | ||
| 70 | getListener().propertyChange(event); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Returns the name of the named property associated with the listener. | ||
| 75 | * | ||
| 76 | * @return the name of the named property associated with the listener | ||
| 77 | */ | ||
| 78 | public String getPropertyName() { | ||
| 79 | return this.propertyName; | ||
| 80 | } | ||
| 81 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,536 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.io.Serializable; | ||
| 28 | import java.io.ObjectStreamField; | ||
| 29 | import java.io.ObjectOutputStream; | ||
| 30 | import java.io.ObjectInputStream; | ||
| 31 | import java.io.IOException; | ||
| 32 | import java.util.Hashtable; | ||
| 33 | import java.util.Map.Entry; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * This is a utility class that can be used by beans that support bound | ||
| 37 | * properties. It manages a list of listeners and dispatches | ||
| 38 | * {@link PropertyChangeEvent}s to them. You can use an instance of this class | ||
| 39 | * as a member field of your bean and delegate these types of work to it. | ||
| 40 | * The {@link PropertyChangeListener} can be registered for all properties | ||
| 41 | * or for a property specified by name. | ||
| 42 | * <p> | ||
| 43 | * Here is an example of {@code PropertyChangeSupport} usage that follows | ||
| 44 | * the rules and recommendations laid out in the JavaBeans™ specification: | ||
| 45 | * <pre> | ||
| 46 | * public class MyBean { | ||
| 47 | * private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); | ||
| 48 | * | ||
| 49 | * public void addPropertyChangeListener(PropertyChangeListener listener) { | ||
| 50 | * this.pcs.addPropertyChangeListener(listener); | ||
| 51 | * } | ||
| 52 | * | ||
| 53 | * public void removePropertyChangeListener(PropertyChangeListener listener) { | ||
| 54 | * this.pcs.removePropertyChangeListener(listener); | ||
| 55 | * } | ||
| 56 | * | ||
| 57 | * private String value; | ||
| 58 | * | ||
| 59 | * public String getValue() { | ||
| 60 | * return this.value; | ||
| 61 | * } | ||
| 62 | * | ||
| 63 | * public void setValue(String newValue) { | ||
| 64 | * String oldValue = this.value; | ||
| 65 | * this.value = newValue; | ||
| 66 | * this.pcs.firePropertyChange("value", oldValue, newValue); | ||
| 67 | * } | ||
| 68 | * | ||
| 69 | * [...] | ||
| 70 | * } | ||
| 71 | * </pre> | ||
| 72 | * <p> | ||
| 73 | * A {@code PropertyChangeSupport} instance is thread-safe. | ||
| 74 | * <p> | ||
| 75 | * This class is serializable. When it is serialized it will save | ||
| 76 | * (and restore) any listeners that are themselves serializable. Any | ||
| 77 | * non-serializable listeners will be skipped during serialization. | ||
| 78 | * | ||
| 79 | * @see VetoableChangeSupport | ||
| 80 | */ | ||
| 81 | public class PropertyChangeSupport implements Serializable { | ||
| 82 | private PropertyChangeListenerMap map = new PropertyChangeListenerMap(); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Constructs a <code>PropertyChangeSupport</code> object. | ||
| 86 | * | ||
| 87 | * @param sourceBean The bean to be given as the source for any events. | ||
| 88 | */ | ||
| 89 | public PropertyChangeSupport(Object sourceBean) { | ||
| 90 | if (sourceBean == null) { | ||
| 91 | throw new NullPointerException(); | ||
| 92 | } | ||
| 93 | source = sourceBean; | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Add a PropertyChangeListener to the listener list. | ||
| 98 | * The listener is registered for all properties. | ||
| 99 | * The same listener object may be added more than once, and will be called | ||
| 100 | * as many times as it is added. | ||
| 101 | * If <code>listener</code> is null, no exception is thrown and no action | ||
| 102 | * is taken. | ||
| 103 | * | ||
| 104 | * @param listener The PropertyChangeListener to be added | ||
| 105 | */ | ||
| 106 | public void addPropertyChangeListener(PropertyChangeListener listener) { | ||
| 107 | if (listener == null) { | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | if (listener instanceof PropertyChangeListenerProxy) { | ||
| 111 | PropertyChangeListenerProxy proxy = | ||
| 112 | (PropertyChangeListenerProxy)listener; | ||
| 113 | // Call two argument add method. | ||
| 114 | addPropertyChangeListener(proxy.getPropertyName(), | ||
| 115 | proxy.getListener()); | ||
| 116 | } else { | ||
| 117 | this.map.add(null, listener); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Remove a PropertyChangeListener from the listener list. | ||
| 123 | * This removes a PropertyChangeListener that was registered | ||
| 124 | * for all properties. | ||
| 125 | * If <code>listener</code> was added more than once to the same event | ||
| 126 | * source, it will be notified one less time after being removed. | ||
| 127 | * If <code>listener</code> is null, or was never added, no exception is | ||
| 128 | * thrown and no action is taken. | ||
| 129 | * | ||
| 130 | * @param listener The PropertyChangeListener to be removed | ||
| 131 | */ | ||
| 132 | public void removePropertyChangeListener(PropertyChangeListener listener) { | ||
| 133 | if (listener == null) { | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | if (listener instanceof PropertyChangeListenerProxy) { | ||
| 137 | PropertyChangeListenerProxy proxy = | ||
| 138 | (PropertyChangeListenerProxy)listener; | ||
| 139 | // Call two argument remove method. | ||
| 140 | removePropertyChangeListener(proxy.getPropertyName(), | ||
| 141 | proxy.getListener()); | ||
| 142 | } else { | ||
| 143 | this.map.remove(null, listener); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Returns an array of all the listeners that were added to the | ||
| 149 | * PropertyChangeSupport object with addPropertyChangeListener(). | ||
| 150 | * <p> | ||
| 151 | * If some listeners have been added with a named property, then | ||
| 152 | * the returned array will be a mixture of PropertyChangeListeners | ||
| 153 | * and <code>PropertyChangeListenerProxy</code>s. If the calling | ||
| 154 | * method is interested in distinguishing the listeners then it must | ||
| 155 | * test each element to see if it's a | ||
| 156 | * <code>PropertyChangeListenerProxy</code>, perform the cast, and examine | ||
| 157 | * the parameter. | ||
| 158 | * | ||
| 159 | * <pre> | ||
| 160 | * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners(); | ||
| 161 | * for (int i = 0; i < listeners.length; i++) { | ||
| 162 | * if (listeners[i] instanceof PropertyChangeListenerProxy) { | ||
| 163 | * PropertyChangeListenerProxy proxy = | ||
| 164 | * (PropertyChangeListenerProxy)listeners[i]; | ||
| 165 | * if (proxy.getPropertyName().equals("foo")) { | ||
| 166 | * // proxy is a PropertyChangeListener which was associated | ||
| 167 | * // with the property named "foo" | ||
| 168 | * } | ||
| 169 | * } | ||
| 170 | * } | ||
| 171 | *</pre> | ||
| 172 | * | ||
| 173 | * @see PropertyChangeListenerProxy | ||
| 174 | * @return all of the <code>PropertyChangeListeners</code> added or an | ||
| 175 | * empty array if no listeners have been added | ||
| 176 | * @since 1.4 | ||
| 177 | */ | ||
| 178 | public PropertyChangeListener[] getPropertyChangeListeners() { | ||
| 179 | return this.map.getListeners(); | ||
| 180 | } | ||
| 181 | |||
| 182 | /** | ||
| 183 | * Add a PropertyChangeListener for a specific property. The listener | ||
| 184 | * will be invoked only when a call on firePropertyChange names that | ||
| 185 | * specific property. | ||
| 186 | * The same listener object may be added more than once. For each | ||
| 187 | * property, the listener will be invoked the number of times it was added | ||
| 188 | * for that property. | ||
| 189 | * If <code>propertyName</code> or <code>listener</code> is null, no | ||
| 190 | * exception is thrown and no action is taken. | ||
| 191 | * | ||
| 192 | * @param propertyName The name of the property to listen on. | ||
| 193 | * @param listener The PropertyChangeListener to be added | ||
| 194 | */ | ||
| 195 | public void addPropertyChangeListener( | ||
| 196 | String propertyName, | ||
| 197 | PropertyChangeListener listener) { | ||
| 198 | if (listener == null || propertyName == null) { | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | listener = this.map.extract(listener); | ||
| 202 | if (listener != null) { | ||
| 203 | this.map.add(propertyName, listener); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * Remove a PropertyChangeListener for a specific property. | ||
| 209 | * If <code>listener</code> was added more than once to the same event | ||
| 210 | * source for the specified property, it will be notified one less time | ||
| 211 | * after being removed. | ||
| 212 | * If <code>propertyName</code> is null, no exception is thrown and no | ||
| 213 | * action is taken. | ||
| 214 | * If <code>listener</code> is null, or was never added for the specified | ||
| 215 | * property, no exception is thrown and no action is taken. | ||
| 216 | * | ||
| 217 | * @param propertyName The name of the property that was listened on. | ||
| 218 | * @param listener The PropertyChangeListener to be removed | ||
| 219 | */ | ||
| 220 | public void removePropertyChangeListener( | ||
| 221 | String propertyName, | ||
| 222 | PropertyChangeListener listener) { | ||
| 223 | if (listener == null || propertyName == null) { | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | listener = this.map.extract(listener); | ||
| 227 | if (listener != null) { | ||
| 228 | this.map.remove(propertyName, listener); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * Returns an array of all the listeners which have been associated | ||
| 234 | * with the named property. | ||
| 235 | * | ||
| 236 | * @param propertyName The name of the property being listened to | ||
| 237 | * @return all of the <code>PropertyChangeListeners</code> associated with | ||
| 238 | * the named property. If no such listeners have been added, | ||
| 239 | * or if <code>propertyName</code> is null, an empty array is | ||
| 240 | * returned. | ||
| 241 | * @since 1.4 | ||
| 242 | */ | ||
| 243 | public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { | ||
| 244 | return this.map.getListeners(propertyName); | ||
| 245 | } | ||
| 246 | |||
| 247 | /** | ||
| 248 | * Reports a bound property update to listeners | ||
| 249 | * that have been registered to track updates of | ||
| 250 | * all properties or a property with the specified name. | ||
| 251 | * <p> | ||
| 252 | * No event is fired if old and new values are equal and non-null. | ||
| 253 | * <p> | ||
| 254 | * This is merely a convenience wrapper around the more general | ||
| 255 | * {@link #firePropertyChange(PropertyChangeEvent)} method. | ||
| 256 | * | ||
| 257 | * @param propertyName the programmatic name of the property that was changed | ||
| 258 | * @param oldValue the old value of the property | ||
| 259 | * @param newValue the new value of the property | ||
| 260 | */ | ||
| 261 | public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { | ||
| 262 | if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { | ||
| 263 | firePropertyChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | /** | ||
| 268 | * Reports an integer bound property update to listeners | ||
| 269 | * that have been registered to track updates of | ||
| 270 | * all properties or a property with the specified name. | ||
| 271 | * <p> | ||
| 272 | * No event is fired if old and new values are equal. | ||
| 273 | * <p> | ||
| 274 | * This is merely a convenience wrapper around the more general | ||
| 275 | * {@link #firePropertyChange(String, Object, Object)} method. | ||
| 276 | * | ||
| 277 | * @param propertyName the programmatic name of the property that was changed | ||
| 278 | * @param oldValue the old value of the property | ||
| 279 | * @param newValue the new value of the property | ||
| 280 | */ | ||
| 281 | public void firePropertyChange(String propertyName, int oldValue, int newValue) { | ||
| 282 | if (oldValue != newValue) { | ||
| 283 | firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | /** | ||
| 288 | * Reports a boolean bound property update to listeners | ||
| 289 | * that have been registered to track updates of | ||
| 290 | * all properties or a property with the specified name. | ||
| 291 | * <p> | ||
| 292 | * No event is fired if old and new values are equal. | ||
| 293 | * <p> | ||
| 294 | * This is merely a convenience wrapper around the more general | ||
| 295 | * {@link #firePropertyChange(String, Object, Object)} method. | ||
| 296 | * | ||
| 297 | * @param propertyName the programmatic name of the property that was changed | ||
| 298 | * @param oldValue the old value of the property | ||
| 299 | * @param newValue the new value of the property | ||
| 300 | */ | ||
| 301 | public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { | ||
| 302 | if (oldValue != newValue) { | ||
| 303 | firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | /** | ||
| 308 | * Fires a property change event to listeners | ||
| 309 | * that have been registered to track updates of | ||
| 310 | * all properties or a property with the specified name. | ||
| 311 | * <p> | ||
| 312 | * No event is fired if the given event's old and new values are equal and non-null. | ||
| 313 | * | ||
| 314 | * @param event the {@code PropertyChangeEvent} to be fired | ||
| 315 | */ | ||
| 316 | public void firePropertyChange(PropertyChangeEvent event) { | ||
| 317 | Object oldValue = event.getOldValue(); | ||
| 318 | Object newValue = event.getNewValue(); | ||
| 319 | if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { | ||
| 320 | String name = event.getPropertyName(); | ||
| 321 | |||
| 322 | PropertyChangeListener[] common = this.map.get(null); | ||
| 323 | PropertyChangeListener[] named = (name != null) | ||
| 324 | ? this.map.get(name) | ||
| 325 | : null; | ||
| 326 | |||
| 327 | fire(common, event); | ||
| 328 | fire(named, event); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | private static void fire(PropertyChangeListener[] listeners, PropertyChangeEvent event) { | ||
| 333 | if (listeners != null) { | ||
| 334 | for (PropertyChangeListener listener : listeners) { | ||
| 335 | listener.propertyChange(event); | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | /** | ||
| 341 | * Reports a bound indexed property update to listeners | ||
| 342 | * that have been registered to track updates of | ||
| 343 | * all properties or a property with the specified name. | ||
| 344 | * <p> | ||
| 345 | * No event is fired if old and new values are equal and non-null. | ||
| 346 | * <p> | ||
| 347 | * This is merely a convenience wrapper around the more general | ||
| 348 | * {@link #firePropertyChange(PropertyChangeEvent)} method. | ||
| 349 | * | ||
| 350 | * @param propertyName the programmatic name of the property that was changed | ||
| 351 | * @param index the index of the property element that was changed | ||
| 352 | * @param oldValue the old value of the property | ||
| 353 | * @param newValue the new value of the property | ||
| 354 | * @since 1.5 | ||
| 355 | */ | ||
| 356 | public void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) { | ||
| 357 | if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { | ||
| 358 | firePropertyChange(new IndexedPropertyChangeEvent(source, propertyName, oldValue, newValue, index)); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | /** | ||
| 363 | * Reports an integer bound indexed property update to listeners | ||
| 364 | * that have been registered to track updates of | ||
| 365 | * all properties or a property with the specified name. | ||
| 366 | * <p> | ||
| 367 | * No event is fired if old and new values are equal. | ||
| 368 | * <p> | ||
| 369 | * This is merely a convenience wrapper around the more general | ||
| 370 | * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method. | ||
| 371 | * | ||
| 372 | * @param propertyName the programmatic name of the property that was changed | ||
| 373 | * @param index the index of the property element that was changed | ||
| 374 | * @param oldValue the old value of the property | ||
| 375 | * @param newValue the new value of the property | ||
| 376 | * @since 1.5 | ||
| 377 | */ | ||
| 378 | public void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) { | ||
| 379 | if (oldValue != newValue) { | ||
| 380 | fireIndexedPropertyChange(propertyName, index, Integer.valueOf(oldValue), Integer.valueOf(newValue)); | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | /** | ||
| 385 | * Reports a boolean bound indexed property update to listeners | ||
| 386 | * that have been registered to track updates of | ||
| 387 | * all properties or a property with the specified name. | ||
| 388 | * <p> | ||
| 389 | * No event is fired if old and new values are equal. | ||
| 390 | * <p> | ||
| 391 | * This is merely a convenience wrapper around the more general | ||
| 392 | * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method. | ||
| 393 | * | ||
| 394 | * @param propertyName the programmatic name of the property that was changed | ||
| 395 | * @param index the index of the property element that was changed | ||
| 396 | * @param oldValue the old value of the property | ||
| 397 | * @param newValue the new value of the property | ||
| 398 | * @since 1.5 | ||
| 399 | */ | ||
| 400 | public void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) { | ||
| 401 | if (oldValue != newValue) { | ||
| 402 | fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * Check if there are any listeners for a specific property, including | ||
| 408 | * those registered on all properties. If <code>propertyName</code> | ||
| 409 | * is null, only check for listeners registered on all properties. | ||
| 410 | * | ||
| 411 | * @param propertyName the property name. | ||
| 412 | * @return true if there are one or more listeners for the given property | ||
| 413 | */ | ||
| 414 | public boolean hasListeners(String propertyName) { | ||
| 415 | return this.map.hasListeners(propertyName); | ||
| 416 | } | ||
| 417 | |||
| 418 | /** | ||
| 419 | * @serialData Null terminated list of <code>PropertyChangeListeners</code>. | ||
| 420 | * <p> | ||
| 421 | * At serialization time we skip non-serializable listeners and | ||
| 422 | * only serialize the serializable listeners. | ||
| 423 | */ | ||
| 424 | private void writeObject(ObjectOutputStream s) throws IOException { | ||
| 425 | Hashtable<String, PropertyChangeSupport> children = null; | ||
| 426 | PropertyChangeListener[] listeners = null; | ||
| 427 | synchronized (this.map) { | ||
| 428 | for (Entry<String, PropertyChangeListener[]> entry : this.map.getEntries()) { | ||
| 429 | String property = entry.getKey(); | ||
| 430 | if (property == null) { | ||
| 431 | listeners = entry.getValue(); | ||
| 432 | } else { | ||
| 433 | if (children == null) { | ||
| 434 | children = new Hashtable<String, PropertyChangeSupport>(); | ||
| 435 | } | ||
| 436 | PropertyChangeSupport pcs = new PropertyChangeSupport(this.source); | ||
| 437 | pcs.map.set(null, entry.getValue()); | ||
| 438 | children.put(property, pcs); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | } | ||
| 442 | ObjectOutputStream.PutField fields = s.putFields(); | ||
| 443 | fields.put("children", children); | ||
| 444 | fields.put("source", this.source); | ||
| 445 | fields.put("propertyChangeSupportSerializedDataVersion", 2); | ||
| 446 | s.writeFields(); | ||
| 447 | |||
| 448 | if (listeners != null) { | ||
| 449 | for (PropertyChangeListener l : listeners) { | ||
| 450 | if (l instanceof Serializable) { | ||
| 451 | s.writeObject(l); | ||
| 452 | } | ||
| 453 | } | ||
| 454 | } | ||
| 455 | s.writeObject(null); | ||
| 456 | } | ||
| 457 | |||
| 458 | private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { | ||
| 459 | this.map = new PropertyChangeListenerMap(); | ||
| 460 | |||
| 461 | ObjectInputStream.GetField fields = s.readFields(); | ||
| 462 | |||
| 463 | Hashtable<String, PropertyChangeSupport> children = (Hashtable<String, PropertyChangeSupport>) fields.get("children", null); | ||
| 464 | this.source = fields.get("source", null); | ||
| 465 | fields.get("propertyChangeSupportSerializedDataVersion", 2); | ||
| 466 | |||
| 467 | Object listenerOrNull; | ||
| 468 | while (null != (listenerOrNull = s.readObject())) { | ||
| 469 | this.map.add(null, (PropertyChangeListener)listenerOrNull); | ||
| 470 | } | ||
| 471 | if (children != null) { | ||
| 472 | for (Entry<String, PropertyChangeSupport> entry : children.entrySet()) { | ||
| 473 | for (PropertyChangeListener listener : entry.getValue().getPropertyChangeListeners()) { | ||
| 474 | this.map.add(entry.getKey(), listener); | ||
| 475 | } | ||
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | /** | ||
| 481 | * The object to be provided as the "source" for any generated events. | ||
| 482 | */ | ||
| 483 | private Object source; | ||
| 484 | |||
| 485 | /** | ||
| 486 | * @serialField children Hashtable | ||
| 487 | * @serialField source Object | ||
| 488 | * @serialField propertyChangeSupportSerializedDataVersion int | ||
| 489 | */ | ||
| 490 | private static final ObjectStreamField[] serialPersistentFields = { | ||
| 491 | new ObjectStreamField("children", Hashtable.class), | ||
| 492 | new ObjectStreamField("source", Object.class), | ||
| 493 | new ObjectStreamField("propertyChangeSupportSerializedDataVersion", Integer.TYPE) | ||
| 494 | }; | ||
| 495 | |||
| 496 | /** | ||
| 497 | * Serialization version ID, so we're compatible with JDK 1.1 | ||
| 498 | */ | ||
| 499 | static final long serialVersionUID = 6401253773779951803L; | ||
| 500 | |||
| 501 | /** | ||
| 502 | * This is a {@link ChangeListenerMap ChangeListenerMap} implementation | ||
| 503 | * that works with {@link PropertyChangeListener PropertyChangeListener} objects. | ||
| 504 | */ | ||
| 505 | private static final class PropertyChangeListenerMap extends ChangeListenerMap<PropertyChangeListener> { | ||
| 506 | private static final PropertyChangeListener[] EMPTY = {}; | ||
| 507 | |||
| 508 | /** | ||
| 509 | * Creates an array of {@link PropertyChangeListener PropertyChangeListener} objects. | ||
| 510 | * This method uses the same instance of the empty array | ||
| 511 | * when {@code length} equals {@code 0}. | ||
| 512 | * | ||
| 513 | * @param length the array length | ||
| 514 | * @return an array with specified length | ||
| 515 | */ | ||
| 516 | @Override | ||
| 517 | protected PropertyChangeListener[] newArray(int length) { | ||
| 518 | return (0 < length) | ||
| 519 | ? new PropertyChangeListener[length] | ||
| 520 | : EMPTY; | ||
| 521 | } | ||
| 522 | |||
| 523 | /** | ||
| 524 | * Creates a {@link PropertyChangeListenerProxy PropertyChangeListenerProxy} | ||
| 525 | * object for the specified property. | ||
| 526 | * | ||
| 527 | * @param name the name of the property to listen on | ||
| 528 | * @param listener the listener to process events | ||
| 529 | * @return a {@code PropertyChangeListenerProxy} object | ||
| 530 | */ | ||
| 531 | @Override | ||
| 532 | protected PropertyChangeListener newProxy(String name, PropertyChangeListener listener) { | ||
| 533 | return new PropertyChangeListenerProxy(name, listener); | ||
| 534 | } | ||
| 535 | } | ||
| 536 | } | ||
Binary file not shown.
| @@ -0,0 +1,719 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.lang.ref.Reference; | ||
| 29 | import java.lang.reflect.Method; | ||
| 30 | import java.lang.reflect.Constructor; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * A PropertyDescriptor describes one property that a Java Bean | ||
| 34 | * exports via a pair of accessor methods. | ||
| 35 | */ | ||
| 36 | public class PropertyDescriptor extends FeatureDescriptor { | ||
| 37 | |||
| 38 | private Reference<Class> propertyTypeRef; | ||
| 39 | private Reference<Method> readMethodRef; | ||
| 40 | private Reference<Method> writeMethodRef; | ||
| 41 | private Reference<Class> propertyEditorClassRef; | ||
| 42 | |||
| 43 | private boolean bound; | ||
| 44 | private boolean constrained; | ||
| 45 | |||
| 46 | // The base name of the method name which will be prefixed with the | ||
| 47 | // read and write method. If name == "foo" then the baseName is "Foo" | ||
| 48 | private String baseName; | ||
| 49 | |||
| 50 | private String writeMethodName; | ||
| 51 | private String readMethodName; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Constructs a PropertyDescriptor for a property that follows | ||
| 55 | * the standard Java convention by having getFoo and setFoo | ||
| 56 | * accessor methods. Thus if the argument name is "fred", it will | ||
| 57 | * assume that the writer method is "setFred" and the reader method | ||
| 58 | * is "getFred" (or "isFred" for a boolean property). Note that the | ||
| 59 | * property name should start with a lower case character, which will | ||
| 60 | * be capitalized in the method names. | ||
| 61 | * | ||
| 62 | * @param propertyName The programmatic name of the property. | ||
| 63 | * @param beanClass The Class object for the target bean. For | ||
| 64 | * example sun.beans.OurButton.class. | ||
| 65 | * @exception IntrospectionException if an exception occurs during | ||
| 66 | * introspection. | ||
| 67 | */ | ||
| 68 | public PropertyDescriptor(String propertyName, Class<?> beanClass) | ||
| 69 | throws IntrospectionException { | ||
| 70 | this(propertyName, beanClass, | ||
| 71 | Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName), | ||
| 72 | Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName)); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * This constructor takes the name of a simple property, and method | ||
| 77 | * names for reading and writing the property. | ||
| 78 | * | ||
| 79 | * @param propertyName The programmatic name of the property. | ||
| 80 | * @param beanClass The Class object for the target bean. For | ||
| 81 | * example sun.beans.OurButton.class. | ||
| 82 | * @param readMethodName The name of the method used for reading the property | ||
| 83 | * value. May be null if the property is write-only. | ||
| 84 | * @param writeMethodName The name of the method used for writing the property | ||
| 85 | * value. May be null if the property is read-only. | ||
| 86 | * @exception IntrospectionException if an exception occurs during | ||
| 87 | * introspection. | ||
| 88 | */ | ||
| 89 | public PropertyDescriptor(String propertyName, Class<?> beanClass, | ||
| 90 | String readMethodName, String writeMethodName) | ||
| 91 | throws IntrospectionException { | ||
| 92 | if (beanClass == null) { | ||
| 93 | throw new IntrospectionException("Target Bean class is null"); | ||
| 94 | } | ||
| 95 | if (propertyName == null || propertyName.length() == 0) { | ||
| 96 | throw new IntrospectionException("bad property name"); | ||
| 97 | } | ||
| 98 | if ("".equals(readMethodName) || "".equals(writeMethodName)) { | ||
| 99 | throw new IntrospectionException("read or write method name should not be the empty string"); | ||
| 100 | } | ||
| 101 | setName(propertyName); | ||
| 102 | setClass0(beanClass); | ||
| 103 | |||
| 104 | this.readMethodName = readMethodName; | ||
| 105 | if (readMethodName != null && getReadMethod() == null) { | ||
| 106 | throw new IntrospectionException("Method not found: " + readMethodName); | ||
| 107 | } | ||
| 108 | this.writeMethodName = writeMethodName; | ||
| 109 | if (writeMethodName != null && getWriteMethod() == null) { | ||
| 110 | throw new IntrospectionException("Method not found: " + writeMethodName); | ||
| 111 | } | ||
| 112 | // If this class or one of its base classes allow PropertyChangeListener, | ||
| 113 | // then we assume that any properties we discover are "bound". | ||
| 114 | // See Introspector.getTargetPropertyInfo() method. | ||
| 115 | Class[] args = { PropertyChangeListener.class }; | ||
| 116 | this.bound = null != Introspector.findMethod(beanClass, "addPropertyChangeListener", args.length, args); | ||
| 117 | } | ||
| 118 | |||
| 119 | /** | ||
| 120 | * This constructor takes the name of a simple property, and Method | ||
| 121 | * objects for reading and writing the property. | ||
| 122 | * | ||
| 123 | * @param propertyName The programmatic name of the property. | ||
| 124 | * @param readMethod The method used for reading the property value. | ||
| 125 | * May be null if the property is write-only. | ||
| 126 | * @param writeMethod The method used for writing the property value. | ||
| 127 | * May be null if the property is read-only. | ||
| 128 | * @exception IntrospectionException if an exception occurs during | ||
| 129 | * introspection. | ||
| 130 | */ | ||
| 131 | public PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod) | ||
| 132 | throws IntrospectionException { | ||
| 133 | if (propertyName == null || propertyName.length() == 0) { | ||
| 134 | throw new IntrospectionException("bad property name"); | ||
| 135 | } | ||
| 136 | setName(propertyName); | ||
| 137 | setReadMethod(readMethod); | ||
| 138 | setWriteMethod(writeMethod); | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Creates <code>PropertyDescriptor</code> for the specified bean | ||
| 143 | * with the specified name and methods to read/write the property value. | ||
| 144 | * | ||
| 145 | * @param bean the type of the target bean | ||
| 146 | * @param base the base name of the property (the rest of the method name) | ||
| 147 | * @param read the method used for reading the property value | ||
| 148 | * @param write the method used for writing the property value | ||
| 149 | * @exception IntrospectionException if an exception occurs during introspection | ||
| 150 | * | ||
| 151 | * @since 1.7 | ||
| 152 | */ | ||
| 153 | PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException { | ||
| 154 | if (bean == null) { | ||
| 155 | throw new IntrospectionException("Target Bean class is null"); | ||
| 156 | } | ||
| 157 | setClass0(bean); | ||
| 158 | setName(Introspector.decapitalize(base)); | ||
| 159 | setReadMethod(read); | ||
| 160 | setWriteMethod(write); | ||
| 161 | this.baseName = base; | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * Returns the Java type info for the property. | ||
| 166 | * Note that the {@code Class} object may describe | ||
| 167 | * primitive Java types such as {@code int}. | ||
| 168 | * This type is returned by the read method | ||
| 169 | * or is used as the parameter type of the write method. | ||
| 170 | * Returns {@code null} if the type is an indexed property | ||
| 171 | * that does not support non-indexed access. | ||
| 172 | * | ||
| 173 | * @return the {@code Class} object that represents the Java type info, | ||
| 174 | * or {@code null} if the type cannot be determined | ||
| 175 | */ | ||
| 176 | public synchronized Class<?> getPropertyType() { | ||
| 177 | Class type = getPropertyType0(); | ||
| 178 | if (type == null) { | ||
| 179 | try { | ||
| 180 | type = findPropertyType(getReadMethod(), getWriteMethod()); | ||
| 181 | setPropertyType(type); | ||
| 182 | } catch (IntrospectionException ex) { | ||
| 183 | // Fall | ||
| 184 | } | ||
| 185 | } | ||
| 186 | return type; | ||
| 187 | } | ||
| 188 | |||
| 189 | private void setPropertyType(Class type) { | ||
| 190 | this.propertyTypeRef = getWeakReference(type); | ||
| 191 | } | ||
| 192 | |||
| 193 | private Class getPropertyType0() { | ||
| 194 | return (this.propertyTypeRef != null) | ||
| 195 | ? this.propertyTypeRef.get() | ||
| 196 | : null; | ||
| 197 | } | ||
| 198 | |||
| 199 | /** | ||
| 200 | * Gets the method that should be used to read the property value. | ||
| 201 | * | ||
| 202 | * @return The method that should be used to read the property value. | ||
| 203 | * May return null if the property can't be read. | ||
| 204 | */ | ||
| 205 | public synchronized Method getReadMethod() { | ||
| 206 | Method readMethod = getReadMethod0(); | ||
| 207 | if (readMethod == null) { | ||
| 208 | Class cls = getClass0(); | ||
| 209 | if (cls == null || (readMethodName == null && readMethodRef == null)) { | ||
| 210 | // The read method was explicitly set to null. | ||
| 211 | return null; | ||
| 212 | } | ||
| 213 | if (readMethodName == null) { | ||
| 214 | Class type = getPropertyType0(); | ||
| 215 | if (type == boolean.class || type == null) { | ||
| 216 | readMethodName = Introspector.IS_PREFIX + getBaseName(); | ||
| 217 | } else { | ||
| 218 | readMethodName = Introspector.GET_PREFIX + getBaseName(); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | // Since there can be multiple write methods but only one getter | ||
| 223 | // method, find the getter method first so that you know what the | ||
| 224 | // property type is. For booleans, there can be "is" and "get" | ||
| 225 | // methods. If an "is" method exists, this is the official | ||
| 226 | // reader method so look for this one first. | ||
| 227 | readMethod = Introspector.findMethod(cls, readMethodName, 0); | ||
| 228 | if (readMethod == null) { | ||
| 229 | readMethodName = Introspector.GET_PREFIX + getBaseName(); | ||
| 230 | readMethod = Introspector.findMethod(cls, readMethodName, 0); | ||
| 231 | } | ||
| 232 | try { | ||
| 233 | setReadMethod(readMethod); | ||
| 234 | } catch (IntrospectionException ex) { | ||
| 235 | // fall | ||
| 236 | } | ||
| 237 | } | ||
| 238 | return readMethod; | ||
| 239 | } | ||
| 240 | |||
| 241 | /** | ||
| 242 | * Sets the method that should be used to read the property value. | ||
| 243 | * | ||
| 244 | * @param readMethod The new read method. | ||
| 245 | */ | ||
| 246 | public synchronized void setReadMethod(Method readMethod) | ||
| 247 | throws IntrospectionException { | ||
| 248 | if (readMethod == null) { | ||
| 249 | readMethodName = null; | ||
| 250 | readMethodRef = null; | ||
| 251 | return; | ||
| 252 | } | ||
| 253 | // The property type is determined by the read method. | ||
| 254 | setPropertyType(findPropertyType(readMethod, getWriteMethod0())); | ||
| 255 | setClass0(readMethod.getDeclaringClass()); | ||
| 256 | |||
| 257 | readMethodName = readMethod.getName(); | ||
| 258 | this.readMethodRef = getSoftReference(readMethod); | ||
| 259 | setTransient(readMethod.getAnnotation(Transient.class)); | ||
| 260 | } | ||
| 261 | |||
| 262 | /** | ||
| 263 | * Gets the method that should be used to write the property value. | ||
| 264 | * | ||
| 265 | * @return The method that should be used to write the property value. | ||
| 266 | * May return null if the property can't be written. | ||
| 267 | */ | ||
| 268 | public synchronized Method getWriteMethod() { | ||
| 269 | Method writeMethod = getWriteMethod0(); | ||
| 270 | if (writeMethod == null) { | ||
| 271 | Class cls = getClass0(); | ||
| 272 | if (cls == null || (writeMethodName == null && writeMethodRef == null)) { | ||
| 273 | // The write method was explicitly set to null. | ||
| 274 | return null; | ||
| 275 | } | ||
| 276 | |||
| 277 | // We need the type to fetch the correct method. | ||
| 278 | Class type = getPropertyType0(); | ||
| 279 | if (type == null) { | ||
| 280 | try { | ||
| 281 | // Can't use getPropertyType since it will lead to recursive loop. | ||
| 282 | type = findPropertyType(getReadMethod(), null); | ||
| 283 | setPropertyType(type); | ||
| 284 | } catch (IntrospectionException ex) { | ||
| 285 | // Without the correct property type we can't be guaranteed | ||
| 286 | // to find the correct method. | ||
| 287 | return null; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | if (writeMethodName == null) { | ||
| 292 | writeMethodName = Introspector.SET_PREFIX + getBaseName(); | ||
| 293 | } | ||
| 294 | |||
| 295 | Class[] args = (type == null) ? null : new Class[] { type }; | ||
| 296 | writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args); | ||
| 297 | if (writeMethod != null) { | ||
| 298 | if (!writeMethod.getReturnType().equals(void.class)) { | ||
| 299 | writeMethod = null; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | try { | ||
| 303 | setWriteMethod(writeMethod); | ||
| 304 | } catch (IntrospectionException ex) { | ||
| 305 | // fall through | ||
| 306 | } | ||
| 307 | } | ||
| 308 | return writeMethod; | ||
| 309 | } | ||
| 310 | |||
| 311 | /** | ||
| 312 | * Sets the method that should be used to write the property value. | ||
| 313 | * | ||
| 314 | * @param writeMethod The new write method. | ||
| 315 | */ | ||
| 316 | public synchronized void setWriteMethod(Method writeMethod) | ||
| 317 | throws IntrospectionException { | ||
| 318 | if (writeMethod == null) { | ||
| 319 | writeMethodName = null; | ||
| 320 | writeMethodRef = null; | ||
| 321 | return; | ||
| 322 | } | ||
| 323 | // Set the property type - which validates the method | ||
| 324 | setPropertyType(findPropertyType(getReadMethod(), writeMethod)); | ||
| 325 | setClass0(writeMethod.getDeclaringClass()); | ||
| 326 | |||
| 327 | writeMethodName = writeMethod.getName(); | ||
| 328 | this.writeMethodRef = getSoftReference(writeMethod); | ||
| 329 | setTransient(writeMethod.getAnnotation(Transient.class)); | ||
| 330 | } | ||
| 331 | |||
| 332 | private Method getReadMethod0() { | ||
| 333 | return (this.readMethodRef != null) | ||
| 334 | ? this.readMethodRef.get() | ||
| 335 | : null; | ||
| 336 | } | ||
| 337 | |||
| 338 | private Method getWriteMethod0() { | ||
| 339 | return (this.writeMethodRef != null) | ||
| 340 | ? this.writeMethodRef.get() | ||
| 341 | : null; | ||
| 342 | } | ||
| 343 | |||
| 344 | /** | ||
| 345 | * Overridden to ensure that a super class doesn't take precedent | ||
| 346 | */ | ||
| 347 | void setClass0(Class clz) { | ||
| 348 | if (getClass0() != null && clz.isAssignableFrom(getClass0())) { | ||
| 349 | // dont replace a subclass with a superclass | ||
| 350 | return; | ||
| 351 | } | ||
| 352 | super.setClass0(clz); | ||
| 353 | } | ||
| 354 | |||
| 355 | /** | ||
| 356 | * Updates to "bound" properties will cause a "PropertyChange" event to | ||
| 357 | * get fired when the property is changed. | ||
| 358 | * | ||
| 359 | * @return True if this is a bound property. | ||
| 360 | */ | ||
| 361 | public boolean isBound() { | ||
| 362 | return bound; | ||
| 363 | } | ||
| 364 | |||
| 365 | /** | ||
| 366 | * Updates to "bound" properties will cause a "PropertyChange" event to | ||
| 367 | * get fired when the property is changed. | ||
| 368 | * | ||
| 369 | * @param bound True if this is a bound property. | ||
| 370 | */ | ||
| 371 | public void setBound(boolean bound) { | ||
| 372 | this.bound = bound; | ||
| 373 | } | ||
| 374 | |||
| 375 | /** | ||
| 376 | * Attempted updates to "Constrained" properties will cause a "VetoableChange" | ||
| 377 | * event to get fired when the property is changed. | ||
| 378 | * | ||
| 379 | * @return True if this is a constrained property. | ||
| 380 | */ | ||
| 381 | public boolean isConstrained() { | ||
| 382 | return constrained; | ||
| 383 | } | ||
| 384 | |||
| 385 | /** | ||
| 386 | * Attempted updates to "Constrained" properties will cause a "VetoableChange" | ||
| 387 | * event to get fired when the property is changed. | ||
| 388 | * | ||
| 389 | * @param constrained True if this is a constrained property. | ||
| 390 | */ | ||
| 391 | public void setConstrained(boolean constrained) { | ||
| 392 | this.constrained = constrained; | ||
| 393 | } | ||
| 394 | |||
| 395 | |||
| 396 | /** | ||
| 397 | * Normally PropertyEditors will be found using the PropertyEditorManager. | ||
| 398 | * However if for some reason you want to associate a particular | ||
| 399 | * PropertyEditor with a given property, then you can do it with | ||
| 400 | * this method. | ||
| 401 | * | ||
| 402 | * @param propertyEditorClass The Class for the desired PropertyEditor. | ||
| 403 | */ | ||
| 404 | public void setPropertyEditorClass(Class<?> propertyEditorClass) { | ||
| 405 | this.propertyEditorClassRef = getWeakReference((Class)propertyEditorClass); | ||
| 406 | } | ||
| 407 | |||
| 408 | /** | ||
| 409 | * Gets any explicit PropertyEditor Class that has been registered | ||
| 410 | * for this property. | ||
| 411 | * | ||
| 412 | * @return Any explicit PropertyEditor Class that has been registered | ||
| 413 | * for this property. Normally this will return "null", | ||
| 414 | * indicating that no special editor has been registered, | ||
| 415 | * so the PropertyEditorManager should be used to locate | ||
| 416 | * a suitable PropertyEditor. | ||
| 417 | */ | ||
| 418 | public Class<?> getPropertyEditorClass() { | ||
| 419 | return (this.propertyEditorClassRef != null) | ||
| 420 | ? this.propertyEditorClassRef.get() | ||
| 421 | : null; | ||
| 422 | } | ||
| 423 | |||
| 424 | /** | ||
| 425 | * Constructs an instance of a property editor using the current | ||
| 426 | * property editor class. | ||
| 427 | * <p> | ||
| 428 | * If the property editor class has a public constructor that takes an | ||
| 429 | * Object argument then it will be invoked using the bean parameter | ||
| 430 | * as the argument. Otherwise, the default constructor will be invoked. | ||
| 431 | * | ||
| 432 | * @param bean the source object | ||
| 433 | * @return a property editor instance or null if a property editor has | ||
| 434 | * not been defined or cannot be created | ||
| 435 | * @since 1.5 | ||
| 436 | */ | ||
| 437 | public PropertyEditor createPropertyEditor(Object bean) { | ||
| 438 | Object editor = null; | ||
| 439 | |||
| 440 | Class cls = getPropertyEditorClass(); | ||
| 441 | if (cls != null) { | ||
| 442 | Constructor ctor = null; | ||
| 443 | if (bean != null) { | ||
| 444 | try { | ||
| 445 | ctor = cls.getConstructor(new Class[] { Object.class }); | ||
| 446 | } catch (Exception ex) { | ||
| 447 | // Fall through | ||
| 448 | } | ||
| 449 | } | ||
| 450 | try { | ||
| 451 | if (ctor == null) { | ||
| 452 | editor = cls.newInstance(); | ||
| 453 | } else { | ||
| 454 | editor = ctor.newInstance(new Object[] { bean }); | ||
| 455 | } | ||
| 456 | } catch (Exception ex) { | ||
| 457 | // A serious error has occured. | ||
| 458 | // Proably due to an invalid property editor. | ||
| 459 | throw new RuntimeException("PropertyEditor not instantiated", | ||
| 460 | ex); | ||
| 461 | } | ||
| 462 | } | ||
| 463 | return (PropertyEditor)editor; | ||
| 464 | } | ||
| 465 | |||
| 466 | |||
| 467 | /** | ||
| 468 | * Compares this <code>PropertyDescriptor</code> against the specified object. | ||
| 469 | * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s | ||
| 470 | * are the same if the read, write, property types, property editor and | ||
| 471 | * flags are equivalent. | ||
| 472 | * | ||
| 473 | * @since 1.4 | ||
| 474 | */ | ||
| 475 | public boolean equals(Object obj) { | ||
| 476 | if (this == obj) { | ||
| 477 | return true; | ||
| 478 | } | ||
| 479 | if (obj != null && obj instanceof PropertyDescriptor) { | ||
| 480 | PropertyDescriptor other = (PropertyDescriptor)obj; | ||
| 481 | Method otherReadMethod = other.getReadMethod(); | ||
| 482 | Method otherWriteMethod = other.getWriteMethod(); | ||
| 483 | |||
| 484 | if (!compareMethods(getReadMethod(), otherReadMethod)) { | ||
| 485 | return false; | ||
| 486 | } | ||
| 487 | |||
| 488 | if (!compareMethods(getWriteMethod(), otherWriteMethod)) { | ||
| 489 | return false; | ||
| 490 | } | ||
| 491 | |||
| 492 | if (getPropertyType() == other.getPropertyType() && | ||
| 493 | getPropertyEditorClass() == other.getPropertyEditorClass() && | ||
| 494 | bound == other.isBound() && constrained == other.isConstrained() && | ||
| 495 | writeMethodName == other.writeMethodName && | ||
| 496 | readMethodName == other.readMethodName) { | ||
| 497 | return true; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | return false; | ||
| 501 | } | ||
| 502 | |||
| 503 | /** | ||
| 504 | * Package private helper method for Descriptor .equals methods. | ||
| 505 | * | ||
| 506 | * @param a first method to compare | ||
| 507 | * @param b second method to compare | ||
| 508 | * @return boolean to indicate that the methods are equivalent | ||
| 509 | */ | ||
| 510 | boolean compareMethods(Method a, Method b) { | ||
| 511 | // Note: perhaps this should be a protected method in FeatureDescriptor | ||
| 512 | if ((a == null) != (b == null)) { | ||
| 513 | return false; | ||
| 514 | } | ||
| 515 | |||
| 516 | if (a != null && b != null) { | ||
| 517 | if (!a.equals(b)) { | ||
| 518 | return false; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | return true; | ||
| 522 | } | ||
| 523 | |||
| 524 | /** | ||
| 525 | * Package-private constructor. | ||
| 526 | * Merge two property descriptors. Where they conflict, give the | ||
| 527 | * second argument (y) priority over the first argument (x). | ||
| 528 | * | ||
| 529 | * @param x The first (lower priority) PropertyDescriptor | ||
| 530 | * @param y The second (higher priority) PropertyDescriptor | ||
| 531 | */ | ||
| 532 | PropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) { | ||
| 533 | super(x,y); | ||
| 534 | |||
| 535 | if (y.baseName != null) { | ||
| 536 | baseName = y.baseName; | ||
| 537 | } else { | ||
| 538 | baseName = x.baseName; | ||
| 539 | } | ||
| 540 | |||
| 541 | if (y.readMethodName != null) { | ||
| 542 | readMethodName = y.readMethodName; | ||
| 543 | } else { | ||
| 544 | readMethodName = x.readMethodName; | ||
| 545 | } | ||
| 546 | |||
| 547 | if (y.writeMethodName != null) { | ||
| 548 | writeMethodName = y.writeMethodName; | ||
| 549 | } else { | ||
| 550 | writeMethodName = x.writeMethodName; | ||
| 551 | } | ||
| 552 | |||
| 553 | if (y.propertyTypeRef != null) { | ||
| 554 | propertyTypeRef = y.propertyTypeRef; | ||
| 555 | } else { | ||
| 556 | propertyTypeRef = x.propertyTypeRef; | ||
| 557 | } | ||
| 558 | |||
| 559 | // Figure out the merged read method. | ||
| 560 | Method xr = x.getReadMethod(); | ||
| 561 | Method yr = y.getReadMethod(); | ||
| 562 | |||
| 563 | // Normally give priority to y's readMethod. | ||
| 564 | try { | ||
| 565 | if (yr != null && yr.getDeclaringClass() == getClass0()) { | ||
| 566 | setReadMethod(yr); | ||
| 567 | } else { | ||
| 568 | setReadMethod(xr); | ||
| 569 | } | ||
| 570 | } catch (IntrospectionException ex) { | ||
| 571 | // fall through | ||
| 572 | } | ||
| 573 | |||
| 574 | // However, if both x and y reference read methods in the same class, | ||
| 575 | // give priority to a boolean "is" method over a boolean "get" method. | ||
| 576 | if (xr != null && yr != null && | ||
| 577 | xr.getDeclaringClass() == yr.getDeclaringClass() && | ||
| 578 | getReturnType(getClass0(), xr) == boolean.class && | ||
| 579 | getReturnType(getClass0(), yr) == boolean.class && | ||
| 580 | xr.getName().indexOf(Introspector.IS_PREFIX) == 0 && | ||
| 581 | yr.getName().indexOf(Introspector.GET_PREFIX) == 0) { | ||
| 582 | try { | ||
| 583 | setReadMethod(xr); | ||
| 584 | } catch (IntrospectionException ex) { | ||
| 585 | // fall through | ||
| 586 | } | ||
| 587 | } | ||
| 588 | |||
| 589 | Method xw = x.getWriteMethod(); | ||
| 590 | Method yw = y.getWriteMethod(); | ||
| 591 | |||
| 592 | try { | ||
| 593 | if (yw != null && yw.getDeclaringClass() == getClass0()) { | ||
| 594 | setWriteMethod(yw); | ||
| 595 | } else { | ||
| 596 | setWriteMethod(xw); | ||
| 597 | } | ||
| 598 | } catch (IntrospectionException ex) { | ||
| 599 | // Fall through | ||
| 600 | } | ||
| 601 | |||
| 602 | if (y.getPropertyEditorClass() != null) { | ||
| 603 | setPropertyEditorClass(y.getPropertyEditorClass()); | ||
| 604 | } else { | ||
| 605 | setPropertyEditorClass(x.getPropertyEditorClass()); | ||
| 606 | } | ||
| 607 | |||
| 608 | |||
| 609 | bound = x.bound | y.bound; | ||
| 610 | constrained = x.constrained | y.constrained; | ||
| 611 | } | ||
| 612 | |||
| 613 | /* | ||
| 614 | * Package-private dup constructor. | ||
| 615 | * This must isolate the new object from any changes to the old object. | ||
| 616 | */ | ||
| 617 | PropertyDescriptor(PropertyDescriptor old) { | ||
| 618 | super(old); | ||
| 619 | propertyTypeRef = old.propertyTypeRef; | ||
| 620 | readMethodRef = old.readMethodRef; | ||
| 621 | writeMethodRef = old.writeMethodRef; | ||
| 622 | propertyEditorClassRef = old.propertyEditorClassRef; | ||
| 623 | |||
| 624 | writeMethodName = old.writeMethodName; | ||
| 625 | readMethodName = old.readMethodName; | ||
| 626 | baseName = old.baseName; | ||
| 627 | |||
| 628 | bound = old.bound; | ||
| 629 | constrained = old.constrained; | ||
| 630 | } | ||
| 631 | |||
| 632 | /** | ||
| 633 | * Returns the property type that corresponds to the read and write method. | ||
| 634 | * The type precedence is given to the readMethod. | ||
| 635 | * | ||
| 636 | * @return the type of the property descriptor or null if both | ||
| 637 | * read and write methods are null. | ||
| 638 | * @throws IntrospectionException if the read or write method is invalid | ||
| 639 | */ | ||
| 640 | private Class findPropertyType(Method readMethod, Method writeMethod) | ||
| 641 | throws IntrospectionException { | ||
| 642 | Class propertyType = null; | ||
| 643 | try { | ||
| 644 | if (readMethod != null) { | ||
| 645 | Class[] params = getParameterTypes(getClass0(), readMethod); | ||
| 646 | if (params.length != 0) { | ||
| 647 | throw new IntrospectionException("bad read method arg count: " | ||
| 648 | + readMethod); | ||
| 649 | } | ||
| 650 | propertyType = getReturnType(getClass0(), readMethod); | ||
| 651 | if (propertyType == Void.TYPE) { | ||
| 652 | throw new IntrospectionException("read method " + | ||
| 653 | readMethod.getName() + " returns void"); | ||
| 654 | } | ||
| 655 | } | ||
| 656 | if (writeMethod != null) { | ||
| 657 | Class params[] = getParameterTypes(getClass0(), writeMethod); | ||
| 658 | if (params.length != 1) { | ||
| 659 | throw new IntrospectionException("bad write method arg count: " | ||
| 660 | + writeMethod); | ||
| 661 | } | ||
| 662 | if (propertyType != null && propertyType != params[0]) { | ||
| 663 | throw new IntrospectionException("type mismatch between read and write methods"); | ||
| 664 | } | ||
| 665 | propertyType = params[0]; | ||
| 666 | } | ||
| 667 | } catch (IntrospectionException ex) { | ||
| 668 | throw ex; | ||
| 669 | } | ||
| 670 | return propertyType; | ||
| 671 | } | ||
| 672 | |||
| 673 | |||
| 674 | /** | ||
| 675 | * Returns a hash code value for the object. | ||
| 676 | * See {@link java.lang.Object#hashCode} for a complete description. | ||
| 677 | * | ||
| 678 | * @return a hash code value for this object. | ||
| 679 | * @since 1.5 | ||
| 680 | */ | ||
| 681 | public int hashCode() { | ||
| 682 | int result = 7; | ||
| 683 | |||
| 684 | result = 37 * result + ((getPropertyType() == null) ? 0 : | ||
| 685 | getPropertyType().hashCode()); | ||
| 686 | result = 37 * result + ((getReadMethod() == null) ? 0 : | ||
| 687 | getReadMethod().hashCode()); | ||
| 688 | result = 37 * result + ((getWriteMethod() == null) ? 0 : | ||
| 689 | getWriteMethod().hashCode()); | ||
| 690 | result = 37 * result + ((getPropertyEditorClass() == null) ? 0 : | ||
| 691 | getPropertyEditorClass().hashCode()); | ||
| 692 | result = 37 * result + ((writeMethodName == null) ? 0 : | ||
| 693 | writeMethodName.hashCode()); | ||
| 694 | result = 37 * result + ((readMethodName == null) ? 0 : | ||
| 695 | readMethodName.hashCode()); | ||
| 696 | result = 37 * result + getName().hashCode(); | ||
| 697 | result = 37 * result + ((bound == false) ? 0 : 1); | ||
| 698 | result = 37 * result + ((constrained == false) ? 0 : 1); | ||
| 699 | |||
| 700 | return result; | ||
| 701 | } | ||
| 702 | |||
| 703 | // Calculate once since capitalize() is expensive. | ||
| 704 | String getBaseName() { | ||
| 705 | if (baseName == null) { | ||
| 706 | baseName = NameGenerator.capitalize(getName()); | ||
| 707 | } | ||
| 708 | return baseName; | ||
| 709 | } | ||
| 710 | |||
| 711 | void appendTo(StringBuilder sb) { | ||
| 712 | appendTo(sb, "bound", this.bound); | ||
| 713 | appendTo(sb, "constrained", this.constrained); | ||
| 714 | appendTo(sb, "propertyEditorClass", this.propertyEditorClassRef); | ||
| 715 | appendTo(sb, "propertyType", this.propertyTypeRef); | ||
| 716 | appendTo(sb, "readMethod", this.readMethodRef); | ||
| 717 | appendTo(sb, "writeMethod", this.writeMethodRef); | ||
| 718 | } | ||
| 719 | } | ||
Binary file not shown.
| @@ -0,0 +1,225 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A PropertyEditor class provides support for GUIs that want to | ||
| 30 | * allow users to edit a property value of a given type. | ||
| 31 | * <p> | ||
| 32 | * PropertyEditor supports a variety of different kinds of ways of | ||
| 33 | * displaying and updating property values. Most PropertyEditors will | ||
| 34 | * only need to support a subset of the different options available in | ||
| 35 | * this API. | ||
| 36 | * <P> | ||
| 37 | * Simple PropertyEditors may only support the getAsText and setAsText | ||
| 38 | * methods and need not support (say) paintValue or getCustomEditor. More | ||
| 39 | * complex types may be unable to support getAsText and setAsText but will | ||
| 40 | * instead support paintValue and getCustomEditor. | ||
| 41 | * <p> | ||
| 42 | * Every propertyEditor must support one or more of the three simple | ||
| 43 | * display styles. Thus it can either (1) support isPaintable or (2) | ||
| 44 | * both return a non-null String[] from getTags() and return a non-null | ||
| 45 | * value from getAsText or (3) simply return a non-null String from | ||
| 46 | * getAsText(). | ||
| 47 | * <p> | ||
| 48 | * Every property editor must support a call on setValue when the argument | ||
| 49 | * object is of the type for which this is the corresponding propertyEditor. | ||
| 50 | * In addition, each property editor must either support a custom editor, | ||
| 51 | * or support setAsText. | ||
| 52 | * <p> | ||
| 53 | * Each PropertyEditor should have a null constructor. | ||
| 54 | */ | ||
| 55 | |||
| 56 | public interface PropertyEditor { | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Set (or change) the object that is to be edited. Primitive types such | ||
| 60 | * as "int" must be wrapped as the corresponding object type such as | ||
| 61 | * "java.lang.Integer". | ||
| 62 | * | ||
| 63 | * @param value The new target object to be edited. Note that this | ||
| 64 | * object should not be modified by the PropertyEditor, rather | ||
| 65 | * the PropertyEditor should create a new object to hold any | ||
| 66 | * modified value. | ||
| 67 | */ | ||
| 68 | void setValue(Object value); | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Gets the property value. | ||
| 72 | * | ||
| 73 | * @return The value of the property. Primitive types such as "int" will | ||
| 74 | * be wrapped as the corresponding object type such as "java.lang.Integer". | ||
| 75 | */ | ||
| 76 | |||
| 77 | Object getValue(); | ||
| 78 | |||
| 79 | //---------------------------------------------------------------------- | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Determines whether this property editor is paintable. | ||
| 83 | * | ||
| 84 | * @return True if the class will honor the paintValue method. | ||
| 85 | */ | ||
| 86 | |||
| 87 | boolean isPaintable(); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Paint a representation of the value into a given area of screen | ||
| 91 | * real estate. Note that the propertyEditor is responsible for doing | ||
| 92 | * its own clipping so that it fits into the given rectangle. | ||
| 93 | * <p> | ||
| 94 | * If the PropertyEditor doesn't honor paint requests (see isPaintable) | ||
| 95 | * this method should be a silent noop. | ||
| 96 | * <p> | ||
| 97 | * The given Graphics object will have the default font, color, etc of | ||
| 98 | * the parent container. The PropertyEditor may change graphics attributes | ||
| 99 | * such as font and color and doesn't need to restore the old values. | ||
| 100 | * | ||
| 101 | * @param gfx Graphics object to paint into. | ||
| 102 | * @param box Rectangle within graphics object into which we should paint. | ||
| 103 | */ | ||
| 104 | void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box); | ||
| 105 | |||
| 106 | //---------------------------------------------------------------------- | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Returns a fragment of Java code that can be used to set a property | ||
| 110 | * to match the editors current state. This method is intended | ||
| 111 | * for use when generating Java code to reflect changes made through the | ||
| 112 | * property editor. | ||
| 113 | * <p> | ||
| 114 | * The code fragment should be context free and must be a legal Java | ||
| 115 | * expression as specified by the JLS. | ||
| 116 | * <p> | ||
| 117 | * Specifically, if the expression represents a computation then all | ||
| 118 | * classes and static members should be fully qualified. This rule | ||
| 119 | * applies to constructors, static methods and non primitive arguments. | ||
| 120 | * <p> | ||
| 121 | * Caution should be used when evaluating the expression as it may throw | ||
| 122 | * exceptions. In particular, code generators must ensure that generated | ||
| 123 | * code will compile in the presence of an expression that can throw | ||
| 124 | * checked exceptions. | ||
| 125 | * <p> | ||
| 126 | * Example results are: | ||
| 127 | * <ul> | ||
| 128 | * <li>Primitive expresssion: <code>2</code> | ||
| 129 | * <li>Class constructor: <code>new java.awt.Color(127,127,34)</code> | ||
| 130 | * <li>Static field: <code>java.awt.Color.orange</code> | ||
| 131 | * <li>Static method: <code>javax.swing.Box.createRigidArea(new | ||
| 132 | * java.awt.Dimension(0, 5))</code> | ||
| 133 | * </ul> | ||
| 134 | * | ||
| 135 | * @return a fragment of Java code representing an initializer for the | ||
| 136 | * current value. It should not contain a semi-colon | ||
| 137 | * ('<code>;</code>') to end the expression. | ||
| 138 | */ | ||
| 139 | String getJavaInitializationString(); | ||
| 140 | |||
| 141 | //---------------------------------------------------------------------- | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Gets the property value as text. | ||
| 145 | * | ||
| 146 | * @return The property value as a human editable string. | ||
| 147 | * <p> Returns null if the value can't be expressed as an editable string. | ||
| 148 | * <p> If a non-null value is returned, then the PropertyEditor should | ||
| 149 | * be prepared to parse that string back in setAsText(). | ||
| 150 | */ | ||
| 151 | String getAsText(); | ||
| 152 | |||
| 153 | /** | ||
| 154 | * Set the property value by parsing a given String. May raise | ||
| 155 | * java.lang.IllegalArgumentException if either the String is | ||
| 156 | * badly formatted or if this kind of property can't be expressed | ||
| 157 | * as text. | ||
| 158 | * @param text The string to be parsed. | ||
| 159 | */ | ||
| 160 | void setAsText(String text) throws java.lang.IllegalArgumentException; | ||
| 161 | |||
| 162 | //---------------------------------------------------------------------- | ||
| 163 | |||
| 164 | /** | ||
| 165 | * If the property value must be one of a set of known tagged values, | ||
| 166 | * then this method should return an array of the tags. This can | ||
| 167 | * be used to represent (for example) enum values. If a PropertyEditor | ||
| 168 | * supports tags, then it should support the use of setAsText with | ||
| 169 | * a tag value as a way of setting the value and the use of getAsText | ||
| 170 | * to identify the current value. | ||
| 171 | * | ||
| 172 | * @return The tag values for this property. May be null if this | ||
| 173 | * property cannot be represented as a tagged value. | ||
| 174 | * | ||
| 175 | */ | ||
| 176 | String[] getTags(); | ||
| 177 | |||
| 178 | //---------------------------------------------------------------------- | ||
| 179 | |||
| 180 | /** | ||
| 181 | * A PropertyEditor may choose to make available a full custom Component | ||
| 182 | * that edits its property value. It is the responsibility of the | ||
| 183 | * PropertyEditor to hook itself up to its editor Component itself and | ||
| 184 | * to report property value changes by firing a PropertyChange event. | ||
| 185 | * <P> | ||
| 186 | * The higher-level code that calls getCustomEditor may either embed | ||
| 187 | * the Component in some larger property sheet, or it may put it in | ||
| 188 | * its own individual dialog, or ... | ||
| 189 | * | ||
| 190 | * @return A java.awt.Component that will allow a human to directly | ||
| 191 | * edit the current property value. May be null if this is | ||
| 192 | * not supported. | ||
| 193 | */ | ||
| 194 | |||
| 195 | java.awt.Component getCustomEditor(); | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Determines whether this property editor supports a custom editor. | ||
| 199 | * | ||
| 200 | * @return True if the propertyEditor can provide a custom editor. | ||
| 201 | */ | ||
| 202 | boolean supportsCustomEditor(); | ||
| 203 | |||
| 204 | //---------------------------------------------------------------------- | ||
| 205 | |||
| 206 | /** | ||
| 207 | * Adds a listener for the value change. | ||
| 208 | * When the property editor changes its value | ||
| 209 | * it should fire a {@link PropertyChangeEvent} | ||
| 210 | * on all registered {@link PropertyChangeListener}s, | ||
| 211 | * specifying the {@code null} value for the property name | ||
| 212 | * and itself as the source. | ||
| 213 | * | ||
| 214 | * @param listener the {@link PropertyChangeListener} to add | ||
| 215 | */ | ||
| 216 | void addPropertyChangeListener(PropertyChangeListener listener); | ||
| 217 | |||
| 218 | /** | ||
| 219 | * Removes a listener for the value change. | ||
| 220 | * | ||
| 221 | * @param listener the {@link PropertyChangeListener} to remove | ||
| 222 | */ | ||
| 223 | void removePropertyChangeListener(PropertyChangeListener listener); | ||
| 224 | |||
| 225 | } | ||
Binary file not shown.
| @@ -0,0 +1,141 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import com.sun.beans.finder.PropertyEditorFinder; | ||
| 29 | import sun.awt.AppContext; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * The PropertyEditorManager can be used to locate a property editor for | ||
| 33 | * any given type name. This property editor must support the | ||
| 34 | * java.beans.PropertyEditor interface for editing a given object. | ||
| 35 | * <P> | ||
| 36 | * The PropertyEditorManager uses three techniques for locating an editor | ||
| 37 | * for a given type. First, it provides a registerEditor method to allow | ||
| 38 | * an editor to be specifically registered for a given type. Second it | ||
| 39 | * tries to locate a suitable class by adding "Editor" to the full | ||
| 40 | * qualified classname of the given type (e.g. "foo.bah.FozEditor"). | ||
| 41 | * Finally it takes the simple classname (without the package name) adds | ||
| 42 | * "Editor" to it and looks in a search-path of packages for a matching | ||
| 43 | * class. | ||
| 44 | * <P> | ||
| 45 | * So for an input class foo.bah.Fred, the PropertyEditorManager would | ||
| 46 | * first look in its tables to see if an editor had been registered for | ||
| 47 | * foo.bah.Fred and if so use that. Then it will look for a | ||
| 48 | * foo.bah.FredEditor class. Then it will look for (say) | ||
| 49 | * standardEditorsPackage.FredEditor class. | ||
| 50 | * <p> | ||
| 51 | * Default PropertyEditors will be provided for the Java primitive types | ||
| 52 | * "boolean", "byte", "short", "int", "long", "float", and "double"; and | ||
| 53 | * for the classes java.lang.String. java.awt.Color, and java.awt.Font. | ||
| 54 | */ | ||
| 55 | |||
| 56 | public class PropertyEditorManager { | ||
| 57 | |||
| 58 | private static final Object FINDER_KEY = new Object(); | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Registers an editor class to edit values of the given target class. | ||
| 62 | * If the editor class is {@code null}, | ||
| 63 | * then any existing definition will be removed. | ||
| 64 | * Thus this method can be used to cancel the registration. | ||
| 65 | * The registration is canceled automatically | ||
| 66 | * if either the target or editor class is unloaded. | ||
| 67 | * <p> | ||
| 68 | * If there is a security manager, its {@code checkPropertiesAccess} | ||
| 69 | * method is called. This could result in a {@linkplain SecurityException}. | ||
| 70 | * | ||
| 71 | * @param targetType the class object of the type to be edited | ||
| 72 | * @param editorClass the class object of the editor class | ||
| 73 | * @throws SecurityException if a security manager exists and | ||
| 74 | * its {@code checkPropertiesAccess} method | ||
| 75 | * doesn't allow setting of system properties | ||
| 76 | * | ||
| 77 | * @see SecurityManager#checkPropertiesAccess | ||
| 78 | */ | ||
| 79 | public static void registerEditor(Class<?> targetType, Class<?> editorClass) { | ||
| 80 | SecurityManager sm = System.getSecurityManager(); | ||
| 81 | if (sm != null) { | ||
| 82 | sm.checkPropertiesAccess(); | ||
| 83 | } | ||
| 84 | getFinder().register(targetType, editorClass); | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Locate a value editor for a given target type. | ||
| 89 | * | ||
| 90 | * @param targetType The Class object for the type to be edited | ||
| 91 | * @return An editor object for the given target class. | ||
| 92 | * The result is null if no suitable editor can be found. | ||
| 93 | */ | ||
| 94 | public static PropertyEditor findEditor(Class<?> targetType) { | ||
| 95 | return getFinder().find(targetType); | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Gets the package names that will be searched for property editors. | ||
| 100 | * | ||
| 101 | * @return The array of package names that will be searched in | ||
| 102 | * order to find property editors. | ||
| 103 | * <p> The default value for this array is implementation-dependent, | ||
| 104 | * e.g. Sun implementation initially sets to {"sun.beans.editors"}. | ||
| 105 | */ | ||
| 106 | public static String[] getEditorSearchPath() { | ||
| 107 | return getFinder().getPackages(); | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Change the list of package names that will be used for | ||
| 112 | * finding property editors. | ||
| 113 | * | ||
| 114 | * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code> | ||
| 115 | * method is called. This could result in a SecurityException. | ||
| 116 | * | ||
| 117 | * @param path Array of package names. | ||
| 118 | * @exception SecurityException if a security manager exists and its | ||
| 119 | * <code>checkPropertiesAccess</code> method doesn't allow setting | ||
| 120 | * of system properties. | ||
| 121 | * @see SecurityManager#checkPropertiesAccess | ||
| 122 | */ | ||
| 123 | public static void setEditorSearchPath(String[] path) { | ||
| 124 | SecurityManager sm = System.getSecurityManager(); | ||
| 125 | if (sm != null) { | ||
| 126 | sm.checkPropertiesAccess(); | ||
| 127 | } | ||
| 128 | getFinder().setPackages(path); | ||
| 129 | } | ||
| 130 | |||
| 131 | private static PropertyEditorFinder getFinder() { | ||
| 132 | AppContext context = AppContext.getAppContext(); | ||
| 133 | Object object = context.get(FINDER_KEY); | ||
| 134 | if (object instanceof PropertyEditorFinder) { | ||
| 135 | return (PropertyEditorFinder) object; | ||
| 136 | } | ||
| 137 | PropertyEditorFinder finder = new PropertyEditorFinder(); | ||
| 138 | context.put(FINDER_KEY, finder); | ||
| 139 | return finder; | ||
| 140 | } | ||
| 141 | } | ||
Binary file not shown.
| @@ -0,0 +1,302 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.beans.*; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * This is a support class to help build property editors. | ||
| 32 | * <p> | ||
| 33 | * It can be used either as a base class or as a delagatee. | ||
| 34 | */ | ||
| 35 | |||
| 36 | public class PropertyEditorSupport implements PropertyEditor { | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Constructs a <code>PropertyEditorSupport</code> object. | ||
| 40 | * | ||
| 41 | * @since 1.5 | ||
| 42 | */ | ||
| 43 | public PropertyEditorSupport() { | ||
| 44 | setSource(this); | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Constructs a <code>PropertyEditorSupport</code> object. | ||
| 49 | * | ||
| 50 | * @param source the source used for event firing | ||
| 51 | * @since 1.5 | ||
| 52 | */ | ||
| 53 | public PropertyEditorSupport(Object source) { | ||
| 54 | if (source == null) { | ||
| 55 | throw new NullPointerException(); | ||
| 56 | } | ||
| 57 | setSource(source); | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Returns the bean that is used as the | ||
| 62 | * source of events. If the source has not | ||
| 63 | * been explicitly set then this instance of | ||
| 64 | * <code>PropertyEditorSupport</code> is returned. | ||
| 65 | * | ||
| 66 | * @return the source object or this instance | ||
| 67 | * @since 1.5 | ||
| 68 | */ | ||
| 69 | public Object getSource() { | ||
| 70 | return source; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Sets the source bean. | ||
| 75 | * <p> | ||
| 76 | * The source bean is used as the source of events | ||
| 77 | * for the property changes. This source should be used for information | ||
| 78 | * purposes only and should not be modified by the PropertyEditor. | ||
| 79 | * | ||
| 80 | * @param source source object to be used for events | ||
| 81 | * @since 1.5 | ||
| 82 | */ | ||
| 83 | public void setSource(Object source) { | ||
| 84 | this.source = source; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Set (or change) the object that is to be edited. | ||
| 89 | * | ||
| 90 | * @param value The new target object to be edited. Note that this | ||
| 91 | * object should not be modified by the PropertyEditor, rather | ||
| 92 | * the PropertyEditor should create a new object to hold any | ||
| 93 | * modified value. | ||
| 94 | */ | ||
| 95 | public void setValue(Object value) { | ||
| 96 | this.value = value; | ||
| 97 | firePropertyChange(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Gets the value of the property. | ||
| 102 | * | ||
| 103 | * @return The value of the property. | ||
| 104 | */ | ||
| 105 | public Object getValue() { | ||
| 106 | return value; | ||
| 107 | } | ||
| 108 | |||
| 109 | //---------------------------------------------------------------------- | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Determines whether the class will honor the paintValue method. | ||
| 113 | * | ||
| 114 | * @return True if the class will honor the paintValue method. | ||
| 115 | */ | ||
| 116 | |||
| 117 | public boolean isPaintable() { | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Paint a representation of the value into a given area of screen | ||
| 123 | * real estate. Note that the propertyEditor is responsible for doing | ||
| 124 | * its own clipping so that it fits into the given rectangle. | ||
| 125 | * <p> | ||
| 126 | * If the PropertyEditor doesn't honor paint requests (see isPaintable) | ||
| 127 | * this method should be a silent noop. | ||
| 128 | * | ||
| 129 | * @param gfx Graphics object to paint into. | ||
| 130 | * @param box Rectangle within graphics object into which we should paint. | ||
| 131 | */ | ||
| 132 | public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { | ||
| 133 | } | ||
| 134 | |||
| 135 | //---------------------------------------------------------------------- | ||
| 136 | |||
| 137 | /** | ||
| 138 | * This method is intended for use when generating Java code to set | ||
| 139 | * the value of the property. It should return a fragment of Java code | ||
| 140 | * that can be used to initialize a variable with the current property | ||
| 141 | * value. | ||
| 142 | * <p> | ||
| 143 | * Example results are "2", "new Color(127,127,34)", "Color.orange", etc. | ||
| 144 | * | ||
| 145 | * @return A fragment of Java code representing an initializer for the | ||
| 146 | * current value. | ||
| 147 | */ | ||
| 148 | public String getJavaInitializationString() { | ||
| 149 | return "???"; | ||
| 150 | } | ||
| 151 | |||
| 152 | //---------------------------------------------------------------------- | ||
| 153 | |||
| 154 | /** | ||
| 155 | * Gets the property value as a string suitable for presentation | ||
| 156 | * to a human to edit. | ||
| 157 | * | ||
| 158 | * @return The property value as a string suitable for presentation | ||
| 159 | * to a human to edit. | ||
| 160 | * <p> Returns null if the value can't be expressed as a string. | ||
| 161 | * <p> If a non-null value is returned, then the PropertyEditor should | ||
| 162 | * be prepared to parse that string back in setAsText(). | ||
| 163 | */ | ||
| 164 | public String getAsText() { | ||
| 165 | return (this.value != null) | ||
| 166 | ? this.value.toString() | ||
| 167 | : null; | ||
| 168 | } | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Sets the property value by parsing a given String. May raise | ||
| 172 | * java.lang.IllegalArgumentException if either the String is | ||
| 173 | * badly formatted or if this kind of property can't be expressed | ||
| 174 | * as text. | ||
| 175 | * | ||
| 176 | * @param text The string to be parsed. | ||
| 177 | */ | ||
| 178 | public void setAsText(String text) throws java.lang.IllegalArgumentException { | ||
| 179 | if (value instanceof String) { | ||
| 180 | setValue(text); | ||
| 181 | return; | ||
| 182 | } | ||
| 183 | throw new java.lang.IllegalArgumentException(text); | ||
| 184 | } | ||
| 185 | |||
| 186 | //---------------------------------------------------------------------- | ||
| 187 | |||
| 188 | /** | ||
| 189 | * If the property value must be one of a set of known tagged values, | ||
| 190 | * then this method should return an array of the tag values. This can | ||
| 191 | * be used to represent (for example) enum values. If a PropertyEditor | ||
| 192 | * supports tags, then it should support the use of setAsText with | ||
| 193 | * a tag value as a way of setting the value. | ||
| 194 | * | ||
| 195 | * @return The tag values for this property. May be null if this | ||
| 196 | * property cannot be represented as a tagged value. | ||
| 197 | * | ||
| 198 | */ | ||
| 199 | public String[] getTags() { | ||
| 200 | return null; | ||
| 201 | } | ||
| 202 | |||
| 203 | //---------------------------------------------------------------------- | ||
| 204 | |||
| 205 | /** | ||
| 206 | * A PropertyEditor may chose to make available a full custom Component | ||
| 207 | * that edits its property value. It is the responsibility of the | ||
| 208 | * PropertyEditor to hook itself up to its editor Component itself and | ||
| 209 | * to report property value changes by firing a PropertyChange event. | ||
| 210 | * <P> | ||
| 211 | * The higher-level code that calls getCustomEditor may either embed | ||
| 212 | * the Component in some larger property sheet, or it may put it in | ||
| 213 | * its own individual dialog, or ... | ||
| 214 | * | ||
| 215 | * @return A java.awt.Component that will allow a human to directly | ||
| 216 | * edit the current property value. May be null if this is | ||
| 217 | * not supported. | ||
| 218 | */ | ||
| 219 | |||
| 220 | public java.awt.Component getCustomEditor() { | ||
| 221 | return null; | ||
| 222 | } | ||
| 223 | |||
| 224 | /** | ||
| 225 | * Determines whether the propertyEditor can provide a custom editor. | ||
| 226 | * | ||
| 227 | * @return True if the propertyEditor can provide a custom editor. | ||
| 228 | */ | ||
| 229 | public boolean supportsCustomEditor() { | ||
| 230 | return false; | ||
| 231 | } | ||
| 232 | |||
| 233 | //---------------------------------------------------------------------- | ||
| 234 | |||
| 235 | /** | ||
| 236 | * Adds a listener for the value change. | ||
| 237 | * When the property editor changes its value | ||
| 238 | * it should fire a {@link PropertyChangeEvent} | ||
| 239 | * on all registered {@link PropertyChangeListener}s, | ||
| 240 | * specifying the {@code null} value for the property name. | ||
| 241 | * If the source property is set, | ||
| 242 | * it should be used as the source of the event. | ||
| 243 | * <p> | ||
| 244 | * The same listener object may be added more than once, | ||
| 245 | * and will be called as many times as it is added. | ||
| 246 | * If {@code listener} is {@code null}, | ||
| 247 | * no exception is thrown and no action is taken. | ||
| 248 | * | ||
| 249 | * @param listener the {@link PropertyChangeListener} to add | ||
| 250 | */ | ||
| 251 | public synchronized void addPropertyChangeListener( | ||
| 252 | PropertyChangeListener listener) { | ||
| 253 | if (listeners == null) { | ||
| 254 | listeners = new java.util.Vector(); | ||
| 255 | } | ||
| 256 | listeners.addElement(listener); | ||
| 257 | } | ||
| 258 | |||
| 259 | /** | ||
| 260 | * Removes a listener for the value change. | ||
| 261 | * <p> | ||
| 262 | * If the same listener was added more than once, | ||
| 263 | * it will be notified one less time after being removed. | ||
| 264 | * If {@code listener} is {@code null}, or was never added, | ||
| 265 | * no exception is thrown and no action is taken. | ||
| 266 | * | ||
| 267 | * @param listener the {@link PropertyChangeListener} to remove | ||
| 268 | */ | ||
| 269 | public synchronized void removePropertyChangeListener( | ||
| 270 | PropertyChangeListener listener) { | ||
| 271 | if (listeners == null) { | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | listeners.removeElement(listener); | ||
| 275 | } | ||
| 276 | |||
| 277 | /** | ||
| 278 | * Report that we have been modified to any interested listeners. | ||
| 279 | */ | ||
| 280 | public void firePropertyChange() { | ||
| 281 | java.util.Vector targets; | ||
| 282 | synchronized (this) { | ||
| 283 | if (listeners == null) { | ||
| 284 | return; | ||
| 285 | } | ||
| 286 | targets = (java.util.Vector) listeners.clone(); | ||
| 287 | } | ||
| 288 | // Tell our listeners that "everything" has changed. | ||
| 289 | PropertyChangeEvent evt = new PropertyChangeEvent(source, null, null, null); | ||
| 290 | |||
| 291 | for (int i = 0; i < targets.size(); i++) { | ||
| 292 | PropertyChangeListener target = (PropertyChangeListener)targets.elementAt(i); | ||
| 293 | target.propertyChange(evt); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | //---------------------------------------------------------------------- | ||
| 298 | |||
| 299 | private Object value; | ||
| 300 | private Object source; | ||
| 301 | private java.util.Vector listeners; | ||
| 302 | } | ||
Binary file not shown.
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | |||
| 29 | /** | ||
| 30 | * A PropertyVetoException is thrown when a proposed change to a | ||
| 31 | * property represents an unacceptable value. | ||
| 32 | */ | ||
| 33 | |||
| 34 | public | ||
| 35 | class PropertyVetoException extends Exception { | ||
| 36 | private static final long serialVersionUID = 129596057694162164L; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Constructs a <code>PropertyVetoException</code> with a | ||
| 40 | * detailed message. | ||
| 41 | * | ||
| 42 | * @param mess Descriptive message | ||
| 43 | * @param evt A PropertyChangeEvent describing the vetoed change. | ||
| 44 | */ | ||
| 45 | public PropertyVetoException(String mess, PropertyChangeEvent evt) { | ||
| 46 | super(mess); | ||
| 47 | this.evt = evt; | ||
| 48 | } | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Gets the vetoed <code>PropertyChangeEvent</code>. | ||
| 52 | * | ||
| 53 | * @return A PropertyChangeEvent describing the vetoed change. | ||
| 54 | */ | ||
| 55 | public PropertyChangeEvent getPropertyChangeEvent() { | ||
| 56 | return evt; | ||
| 57 | } | ||
| 58 | |||
| 59 | /** | ||
| 60 | * A PropertyChangeEvent describing the vetoed change. | ||
| 61 | * @serial | ||
| 62 | */ | ||
| 63 | private PropertyChangeEvent evt; | ||
| 64 | } | ||
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,75 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.lang.reflect.Field; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * A utility class for reflectively finding methods, constuctors and fields | ||
| 31 | * using reflection. | ||
| 32 | */ | ||
| 33 | class ReflectionUtils { | ||
| 34 | |||
| 35 | public static boolean isPrimitive(Class type) { | ||
| 36 | return primitiveTypeFor(type) != null; | ||
| 37 | } | ||
| 38 | |||
| 39 | public static Class primitiveTypeFor(Class wrapper) { | ||
| 40 | if (wrapper == Boolean.class) return Boolean.TYPE; | ||
| 41 | if (wrapper == Byte.class) return Byte.TYPE; | ||
| 42 | if (wrapper == Character.class) return Character.TYPE; | ||
| 43 | if (wrapper == Short.class) return Short.TYPE; | ||
| 44 | if (wrapper == Integer.class) return Integer.TYPE; | ||
| 45 | if (wrapper == Long.class) return Long.TYPE; | ||
| 46 | if (wrapper == Float.class) return Float.TYPE; | ||
| 47 | if (wrapper == Double.class) return Double.TYPE; | ||
| 48 | if (wrapper == Void.class) return Void.TYPE; | ||
| 49 | return null; | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Returns the value of a private field. | ||
| 54 | * | ||
| 55 | * @param instance object instance | ||
| 56 | * @param cls class | ||
| 57 | * @param name name of the field | ||
| 58 | * @param el an exception listener to handle exceptions; or null | ||
| 59 | * @return value of the field; null if not found or an error is encountered | ||
| 60 | */ | ||
| 61 | public static Object getPrivateField(Object instance, Class cls, | ||
| 62 | String name, ExceptionListener el) { | ||
| 63 | try { | ||
| 64 | Field f = cls.getDeclaredField(name); | ||
| 65 | f.setAccessible(true); | ||
| 66 | return f.get(instance); | ||
| 67 | } | ||
| 68 | catch (Exception e) { | ||
| 69 | if (el != null) { | ||
| 70 | el.exceptionThrown(e); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | return null; | ||
| 74 | } | ||
| 75 | } | ||
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,146 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * This is a support class to make it easier for people to provide | ||
| 30 | * BeanInfo classes. | ||
| 31 | * <p> | ||
| 32 | * It defaults to providing "noop" information, and can be selectively | ||
| 33 | * overriden to provide more explicit information on chosen topics. | ||
| 34 | * When the introspector sees the "noop" values, it will apply low | ||
| 35 | * level introspection and design patterns to automatically analyze | ||
| 36 | * the target bean. | ||
| 37 | */ | ||
| 38 | |||
| 39 | public class SimpleBeanInfo implements BeanInfo { | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Deny knowledge about the class and customizer of the bean. | ||
| 43 | * You can override this if you wish to provide explicit info. | ||
| 44 | */ | ||
| 45 | public BeanDescriptor getBeanDescriptor() { | ||
| 46 | return null; | ||
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Deny knowledge of properties. You can override this | ||
| 51 | * if you wish to provide explicit property info. | ||
| 52 | */ | ||
| 53 | public PropertyDescriptor[] getPropertyDescriptors() { | ||
| 54 | return null; | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Deny knowledge of a default property. You can override this | ||
| 59 | * if you wish to define a default property for the bean. | ||
| 60 | */ | ||
| 61 | public int getDefaultPropertyIndex() { | ||
| 62 | return -1; | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Deny knowledge of event sets. You can override this | ||
| 67 | * if you wish to provide explicit event set info. | ||
| 68 | */ | ||
| 69 | public EventSetDescriptor[] getEventSetDescriptors() { | ||
| 70 | return null; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Deny knowledge of a default event. You can override this | ||
| 75 | * if you wish to define a default event for the bean. | ||
| 76 | */ | ||
| 77 | public int getDefaultEventIndex() { | ||
| 78 | return -1; | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Deny knowledge of methods. You can override this | ||
| 83 | * if you wish to provide explicit method info. | ||
| 84 | */ | ||
| 85 | public MethodDescriptor[] getMethodDescriptors() { | ||
| 86 | return null; | ||
| 87 | } | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Claim there are no other relevant BeanInfo objects. You | ||
| 91 | * may override this if you want to (for example) return a | ||
| 92 | * BeanInfo for a base class. | ||
| 93 | */ | ||
| 94 | public BeanInfo[] getAdditionalBeanInfo() { | ||
| 95 | return null; | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Claim there are no icons available. You can override | ||
| 100 | * this if you want to provide icons for your bean. | ||
| 101 | */ | ||
| 102 | public java.awt.Image getIcon(int iconKind) { | ||
| 103 | return null; | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * This is a utility method to help in loading icon images. | ||
| 108 | * It takes the name of a resource file associated with the | ||
| 109 | * current object's class file and loads an image object | ||
| 110 | * from that file. Typically images will be GIFs. | ||
| 111 | * <p> | ||
| 112 | * @param resourceName A pathname relative to the directory | ||
| 113 | * holding the class file of the current class. For example, | ||
| 114 | * "wombat.gif". | ||
| 115 | * @return an image object. May be null if the load failed. | ||
| 116 | */ | ||
| 117 | public java.awt.Image loadImage(final String resourceName) { | ||
| 118 | try { | ||
| 119 | final Class c = getClass(); | ||
| 120 | java.awt.image.ImageProducer ip = (java.awt.image.ImageProducer) | ||
| 121 | java.security.AccessController.doPrivileged( | ||
| 122 | new java.security.PrivilegedAction() { | ||
| 123 | public Object run() { | ||
| 124 | java.net.URL url; | ||
| 125 | if ((url = c.getResource(resourceName)) == null) { | ||
| 126 | return null; | ||
| 127 | } else { | ||
| 128 | try { | ||
| 129 | return url.getContent(); | ||
| 130 | } catch (java.io.IOException ioe) { | ||
| 131 | return null; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | }); | ||
| 136 | |||
| 137 | if (ip == null) | ||
| 138 | return null; | ||
| 139 | java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit(); | ||
| 140 | return tk.createImage(ip); | ||
| 141 | } catch (Exception ex) { | ||
| 142 | return null; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,363 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.lang.reflect.AccessibleObject; | ||
| 28 | import java.lang.reflect.Array; | ||
| 29 | import java.lang.reflect.Constructor; | ||
| 30 | import java.lang.reflect.InvocationTargetException; | ||
| 31 | import java.lang.reflect.Method; | ||
| 32 | import java.security.AccessControlContext; | ||
| 33 | import java.security.AccessController; | ||
| 34 | import java.security.PrivilegedActionException; | ||
| 35 | import java.security.PrivilegedExceptionAction; | ||
| 36 | |||
| 37 | import com.sun.beans.finder.ClassFinder; | ||
| 38 | import com.sun.beans.finder.ConstructorFinder; | ||
| 39 | import com.sun.beans.finder.MethodFinder; | ||
| 40 | import sun.reflect.misc.MethodUtil; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * A <code>Statement</code> object represents a primitive statement | ||
| 44 | * in which a single method is applied to a target and | ||
| 45 | * a set of arguments - as in <code>"a.setFoo(b)"</code>. | ||
| 46 | * Note that where this example uses names | ||
| 47 | * to denote the target and its argument, a statement | ||
| 48 | * object does not require a name space and is constructed with | ||
| 49 | * the values themselves. | ||
| 50 | * The statement object associates the named method | ||
| 51 | * with its environment as a simple set of values: | ||
| 52 | * the target and an array of argument values. | ||
| 53 | * | ||
| 54 | * @since 1.4 | ||
| 55 | * | ||
| 56 | * @author Philip Milne | ||
| 57 | */ | ||
| 58 | public class Statement { | ||
| 59 | |||
| 60 | private static Object[] emptyArray = new Object[]{}; | ||
| 61 | |||
| 62 | static ExceptionListener defaultExceptionListener = new ExceptionListener() { | ||
| 63 | public void exceptionThrown(Exception e) { | ||
| 64 | System.err.println(e); | ||
| 65 | // e.printStackTrace(); | ||
| 66 | System.err.println("Continuing ..."); | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | |||
| 70 | private final AccessControlContext acc = AccessController.getContext(); | ||
| 71 | private final Object target; | ||
| 72 | private final String methodName; | ||
| 73 | private final Object[] arguments; | ||
| 74 | ClassLoader loader; | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Creates a new {@link Statement} object | ||
| 78 | * for the specified target object to invoke the method | ||
| 79 | * specified by the name and by the array of arguments. | ||
| 80 | * <p> | ||
| 81 | * The {@code target} and the {@code methodName} values should not be {@code null}. | ||
| 82 | * Otherwise an attempt to execute this {@code Expression} | ||
| 83 | * will result in a {@code NullPointerException}. | ||
| 84 | * If the {@code arguments} value is {@code null}, | ||
| 85 | * an empty array is used as the value of the {@code arguments} property. | ||
| 86 | * | ||
| 87 | * @param target the target object of this statement | ||
| 88 | * @param methodName the name of the method to invoke on the specified target | ||
| 89 | * @param arguments the array of arguments to invoke the specified method | ||
| 90 | */ | ||
| 91 | @ConstructorProperties({"target", "methodName", "arguments"}) | ||
| 92 | public Statement(Object target, String methodName, Object[] arguments) { | ||
| 93 | this.target = target; | ||
| 94 | this.methodName = methodName; | ||
| 95 | this.arguments = (arguments == null) ? emptyArray : arguments; | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Returns the target object of this statement. | ||
| 100 | * If this method returns {@code null}, | ||
| 101 | * the {@link #execute} method | ||
| 102 | * throws a {@code NullPointerException}. | ||
| 103 | * | ||
| 104 | * @return the target object of this statement | ||
| 105 | */ | ||
| 106 | public Object getTarget() { | ||
| 107 | return target; | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Returns the name of the method to invoke. | ||
| 112 | * If this method returns {@code null}, | ||
| 113 | * the {@link #execute} method | ||
| 114 | * throws a {@code NullPointerException}. | ||
| 115 | * | ||
| 116 | * @return the name of the method | ||
| 117 | */ | ||
| 118 | public String getMethodName() { | ||
| 119 | return methodName; | ||
| 120 | } | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Returns the arguments for the method to invoke. | ||
| 124 | * The number of arguments and their types | ||
| 125 | * must match the method being called. | ||
| 126 | * {@code null} can be used as a synonym of an empty array. | ||
| 127 | * | ||
| 128 | * @return the array of arguments | ||
| 129 | */ | ||
| 130 | public Object[] getArguments() { | ||
| 131 | return arguments; | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * The {@code execute} method finds a method whose name is the same | ||
| 136 | * as the {@code methodName} property, and invokes the method on | ||
| 137 | * the target. | ||
| 138 | * | ||
| 139 | * When the target's class defines many methods with the given name | ||
| 140 | * the implementation should choose the most specific method using | ||
| 141 | * the algorithm specified in the Java Language Specification | ||
| 142 | * (15.11). The dynamic class of the target and arguments are used | ||
| 143 | * in place of the compile-time type information and, like the | ||
| 144 | * {@link java.lang.reflect.Method} class itself, conversion between | ||
| 145 | * primitive values and their associated wrapper classes is handled | ||
| 146 | * internally. | ||
| 147 | * <p> | ||
| 148 | * The following method types are handled as special cases: | ||
| 149 | * <ul> | ||
| 150 | * <li> | ||
| 151 | * Static methods may be called by using a class object as the target. | ||
| 152 | * <li> | ||
| 153 | * The reserved method name "new" may be used to call a class's constructor | ||
| 154 | * as if all classes defined static "new" methods. Constructor invocations | ||
| 155 | * are typically considered {@code Expression}s rather than {@code Statement}s | ||
| 156 | * as they return a value. | ||
| 157 | * <li> | ||
| 158 | * The method names "get" and "set" defined in the {@link java.util.List} | ||
| 159 | * interface may also be applied to array instances, mapping to | ||
| 160 | * the static methods of the same name in the {@code Array} class. | ||
| 161 | * </ul> | ||
| 162 | * | ||
| 163 | * @throws NullPointerException if the value of the {@code target} or | ||
| 164 | * {@code methodName} property is {@code null} | ||
| 165 | * @throws NoSuchMethodException if a matching method is not found | ||
| 166 | * @throws SecurityException if a security manager exists and | ||
| 167 | * it denies the method invocation | ||
| 168 | * @throws Exception that is thrown by the invoked method | ||
| 169 | * | ||
| 170 | * @see java.lang.reflect.Method | ||
| 171 | */ | ||
| 172 | public void execute() throws Exception { | ||
| 173 | invoke(); | ||
| 174 | } | ||
| 175 | |||
| 176 | Object invoke() throws Exception { | ||
| 177 | AccessControlContext acc = this.acc; | ||
| 178 | if ((acc == null) && (System.getSecurityManager() != null)) { | ||
| 179 | throw new SecurityException("AccessControlContext is not set"); | ||
| 180 | } | ||
| 181 | try { | ||
| 182 | return AccessController.doPrivileged( | ||
| 183 | new PrivilegedExceptionAction<Object>() { | ||
| 184 | public Object run() throws Exception { | ||
| 185 | return invokeInternal(); | ||
| 186 | } | ||
| 187 | }, | ||
| 188 | acc | ||
| 189 | ); | ||
| 190 | } | ||
| 191 | catch (PrivilegedActionException exception) { | ||
| 192 | throw exception.getException(); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | private Object invokeInternal() throws Exception { | ||
| 197 | Object target = getTarget(); | ||
| 198 | String methodName = getMethodName(); | ||
| 199 | |||
| 200 | if (target == null || methodName == null) { | ||
| 201 | throw new NullPointerException((target == null ? "target" : | ||
| 202 | "methodName") + " should not be null"); | ||
| 203 | } | ||
| 204 | |||
| 205 | Object[] arguments = getArguments(); | ||
| 206 | if (arguments == null) { | ||
| 207 | arguments = emptyArray; | ||
| 208 | } | ||
| 209 | // Class.forName() won't load classes outside | ||
| 210 | // of core from a class inside core. Special | ||
| 211 | // case this method. | ||
| 212 | if (target == Class.class && methodName.equals("forName")) { | ||
| 213 | return ClassFinder.resolveClass((String)arguments[0], this.loader); | ||
| 214 | } | ||
| 215 | Class[] argClasses = new Class[arguments.length]; | ||
| 216 | for(int i = 0; i < arguments.length; i++) { | ||
| 217 | argClasses[i] = (arguments[i] == null) ? null : arguments[i].getClass(); | ||
| 218 | } | ||
| 219 | |||
| 220 | AccessibleObject m = null; | ||
| 221 | if (target instanceof Class) { | ||
| 222 | /* | ||
| 223 | For class methods, simluate the effect of a meta class | ||
| 224 | by taking the union of the static methods of the | ||
| 225 | actual class, with the instance methods of "Class.class" | ||
| 226 | and the overloaded "newInstance" methods defined by the | ||
| 227 | constructors. | ||
| 228 | This way "System.class", for example, will perform both | ||
| 229 | the static method getProperties() and the instance method | ||
| 230 | getSuperclass() defined in "Class.class". | ||
| 231 | */ | ||
| 232 | if (methodName.equals("new")) { | ||
| 233 | methodName = "newInstance"; | ||
| 234 | } | ||
| 235 | // Provide a short form for array instantiation by faking an nary-constructor. | ||
| 236 | if (methodName.equals("newInstance") && ((Class)target).isArray()) { | ||
| 237 | Object result = Array.newInstance(((Class)target).getComponentType(), arguments.length); | ||
| 238 | for(int i = 0; i < arguments.length; i++) { | ||
| 239 | Array.set(result, i, arguments[i]); | ||
| 240 | } | ||
| 241 | return result; | ||
| 242 | } | ||
| 243 | if (methodName.equals("newInstance") && arguments.length != 0) { | ||
| 244 | // The Character class, as of 1.4, does not have a constructor | ||
| 245 | // which takes a String. All of the other "wrapper" classes | ||
| 246 | // for Java's primitive types have a String constructor so we | ||
| 247 | // fake such a constructor here so that this special case can be | ||
| 248 | // ignored elsewhere. | ||
| 249 | if (target == Character.class && arguments.length == 1 && | ||
| 250 | argClasses[0] == String.class) { | ||
| 251 | return new Character(((String)arguments[0]).charAt(0)); | ||
| 252 | } | ||
| 253 | try { | ||
| 254 | m = ConstructorFinder.findConstructor((Class)target, argClasses); | ||
| 255 | } | ||
| 256 | catch (NoSuchMethodException exception) { | ||
| 257 | m = null; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | if (m == null && target != Class.class) { | ||
| 261 | m = getMethod((Class)target, methodName, argClasses); | ||
| 262 | } | ||
| 263 | if (m == null) { | ||
| 264 | m = getMethod(Class.class, methodName, argClasses); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | else { | ||
| 268 | /* | ||
| 269 | This special casing of arrays is not necessary, but makes files | ||
| 270 | involving arrays much shorter and simplifies the archiving infrastrcure. | ||
| 271 | The Array.set() method introduces an unusual idea - that of a static method | ||
| 272 | changing the state of an instance. Normally statements with side | ||
| 273 | effects on objects are instance methods of the objects themselves | ||
| 274 | and we reinstate this rule (perhaps temporarily) by special-casing arrays. | ||
| 275 | */ | ||
| 276 | if (target.getClass().isArray() && | ||
| 277 | (methodName.equals("set") || methodName.equals("get"))) { | ||
| 278 | int index = ((Integer)arguments[0]).intValue(); | ||
| 279 | if (methodName.equals("get")) { | ||
| 280 | return Array.get(target, index); | ||
| 281 | } | ||
| 282 | else { | ||
| 283 | Array.set(target, index, arguments[1]); | ||
| 284 | return null; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | m = getMethod(target.getClass(), methodName, argClasses); | ||
| 288 | } | ||
| 289 | if (m != null) { | ||
| 290 | try { | ||
| 291 | if (m instanceof Method) { | ||
| 292 | return MethodUtil.invoke((Method)m, target, arguments); | ||
| 293 | } | ||
| 294 | else { | ||
| 295 | return ((Constructor)m).newInstance(arguments); | ||
| 296 | } | ||
| 297 | } | ||
| 298 | catch (IllegalAccessException iae) { | ||
| 299 | throw new Exception("Statement cannot invoke: " + | ||
| 300 | methodName + " on " + target.getClass(), | ||
| 301 | iae); | ||
| 302 | } | ||
| 303 | catch (InvocationTargetException ite) { | ||
| 304 | Throwable te = ite.getTargetException(); | ||
| 305 | if (te instanceof Exception) { | ||
| 306 | throw (Exception)te; | ||
| 307 | } | ||
| 308 | else { | ||
| 309 | throw ite; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | throw new NoSuchMethodException(toString()); | ||
| 314 | } | ||
| 315 | |||
| 316 | String instanceName(Object instance) { | ||
| 317 | if (instance == null) { | ||
| 318 | return "null"; | ||
| 319 | } else if (instance.getClass() == String.class) { | ||
| 320 | return "\""+(String)instance + "\""; | ||
| 321 | } else { | ||
| 322 | // Note: there is a minor problem with using the non-caching | ||
| 323 | // NameGenerator method. The return value will not have | ||
| 324 | // specific information about the inner class name. For example, | ||
| 325 | // In 1.4.2 an inner class would be represented as JList$1 now | ||
| 326 | // would be named Class. | ||
| 327 | |||
| 328 | return NameGenerator.unqualifiedClassName(instance.getClass()); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | /** | ||
| 333 | * Prints the value of this statement using a Java-style syntax. | ||
| 334 | */ | ||
| 335 | public String toString() { | ||
| 336 | // Respect a subclass's implementation here. | ||
| 337 | Object target = getTarget(); | ||
| 338 | String methodName = getMethodName(); | ||
| 339 | Object[] arguments = getArguments(); | ||
| 340 | if (arguments == null) { | ||
| 341 | arguments = emptyArray; | ||
| 342 | } | ||
| 343 | StringBuffer result = new StringBuffer(instanceName(target) + "." + methodName + "("); | ||
| 344 | int n = arguments.length; | ||
| 345 | for(int i = 0; i < n; i++) { | ||
| 346 | result.append(instanceName(arguments[i])); | ||
| 347 | if (i != n -1) { | ||
| 348 | result.append(", "); | ||
| 349 | } | ||
| 350 | } | ||
| 351 | result.append(");"); | ||
| 352 | return result.toString(); | ||
| 353 | } | ||
| 354 | |||
| 355 | static Method getMethod(Class<?> type, String name, Class<?>... args) { | ||
| 356 | try { | ||
| 357 | return MethodFinder.findMethod(type, name, args); | ||
| 358 | } | ||
| 359 | catch (NoSuchMethodException exception) { | ||
| 360 | return null; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } | ||
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,68 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.lang.annotation.Retention; | ||
| 29 | import java.lang.annotation.Target; | ||
| 30 | |||
| 31 | import static java.lang.annotation.ElementType.METHOD; | ||
| 32 | import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Indicates that an attribute called "transient" | ||
| 36 | * should be declared with the given {@code value} | ||
| 37 | * when the {@link Introspector} constructs | ||
| 38 | * a {@link PropertyDescriptor} or {@link EventSetDescriptor} | ||
| 39 | * classes associated with the annotated code element. | ||
| 40 | * A {@code true} value for the "transient" attribute | ||
| 41 | * indicates to encoders derived from {@link Encoder} | ||
| 42 | * that this feature should be ignored. | ||
| 43 | * <p/> | ||
| 44 | * The {@code Transient} annotation may be be used | ||
| 45 | * in any of the methods that are involved | ||
| 46 | * in a {@link FeatureDescriptor} subclass | ||
| 47 | * to identify the transient feature in the annotated class and its subclasses. | ||
| 48 | * Normally, the method that starts with "get" is the best place | ||
| 49 | * to put the annotation and it is this declaration | ||
| 50 | * that takes precedence in the case of multiple annotations | ||
| 51 | * being defined for the same feature. | ||
| 52 | * <p/> | ||
| 53 | * To declare a feature non-transient in a class | ||
| 54 | * whose superclass declares it transient, | ||
| 55 | * use {@code @Transient(false)}. | ||
| 56 | * In all cases, the {@link Introspector} decides | ||
| 57 | * if a feature is transient by referring to the annotation | ||
| 58 | * on the most specific superclass. | ||
| 59 | * If no {@code Transient} annotation is present | ||
| 60 | * in any superclass the feature is not transient. | ||
| 61 | * | ||
| 62 | * @since 1.7 | ||
| 63 | */ | ||
| 64 | @Target({METHOD}) | ||
| 65 | @Retention(RUNTIME) | ||
| 66 | public @interface Transient { | ||
| 67 | boolean value() default true; | ||
| 68 | } | ||
Binary file not shown.
| @@ -0,0 +1,44 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 1997, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A VetoableChange event gets fired whenever a bean changes a "constrained" | ||
| 30 | * property. You can register a VetoableChangeListener with a source bean | ||
| 31 | * so as to be notified of any constrained property updates. | ||
| 32 | */ | ||
| 33 | public interface VetoableChangeListener extends java.util.EventListener { | ||
| 34 | /** | ||
| 35 | * This method gets called when a constrained property is changed. | ||
| 36 | * | ||
| 37 | * @param evt a <code>PropertyChangeEvent</code> object describing the | ||
| 38 | * event source and the property that has changed. | ||
| 39 | * @exception PropertyVetoException if the recipient wishes the property | ||
| 40 | * change to be rolled back. | ||
| 41 | */ | ||
| 42 | void vetoableChange(PropertyChangeEvent evt) | ||
| 43 | throws PropertyVetoException; | ||
| 44 | } | ||
Binary file not shown.
| @@ -0,0 +1,84 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | import java.util.EventListenerProxy; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A class which extends the {@code EventListenerProxy} | ||
| 32 | * specifically for adding a {@code VetoableChangeListener} | ||
| 33 | * with a "constrained" property. | ||
| 34 | * Instances of this class can be added | ||
| 35 | * as {@code VetoableChangeListener}s to a bean | ||
| 36 | * which supports firing vetoable change events. | ||
| 37 | * <p> | ||
| 38 | * If the object has a {@code getVetoableChangeListeners} method | ||
| 39 | * then the array returned could be a mixture of {@code VetoableChangeListener} | ||
| 40 | * and {@code VetoableChangeListenerProxy} objects. | ||
| 41 | * | ||
| 42 | * @see java.util.EventListenerProxy | ||
| 43 | * @see VetoableChangeSupport#getVetoableChangeListeners | ||
| 44 | * @since 1.4 | ||
| 45 | */ | ||
| 46 | public class VetoableChangeListenerProxy | ||
| 47 | extends EventListenerProxy<VetoableChangeListener> | ||
| 48 | implements VetoableChangeListener { | ||
| 49 | |||
| 50 | private final String propertyName; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Constructor which binds the {@code VetoableChangeListener} | ||
| 54 | * to a specific property. | ||
| 55 | * | ||
| 56 | * @param propertyName the name of the property to listen on | ||
| 57 | * @param listener the listener object | ||
| 58 | */ | ||
| 59 | public VetoableChangeListenerProxy(String propertyName, VetoableChangeListener listener) { | ||
| 60 | super(listener); | ||
| 61 | this.propertyName = propertyName; | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Forwards the property change event to the listener delegate. | ||
| 66 | * | ||
| 67 | * @param event the property change event | ||
| 68 | * | ||
| 69 | * @exception PropertyVetoException if the recipient wishes the property | ||
| 70 | * change to be rolled back | ||
| 71 | */ | ||
| 72 | public void vetoableChange(PropertyChangeEvent event) throws PropertyVetoException{ | ||
| 73 | getListener().vetoableChange(event); | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Returns the name of the named property associated with the listener. | ||
| 78 | * | ||
| 79 | * @return the name of the named property associated with the listener | ||
| 80 | */ | ||
| 81 | public String getPropertyName() { | ||
| 82 | return this.propertyName; | ||
| 83 | } | ||
| 84 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,525 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.io.Serializable; | ||
| 28 | import java.io.ObjectStreamField; | ||
| 29 | import java.io.ObjectOutputStream; | ||
| 30 | import java.io.ObjectInputStream; | ||
| 31 | import java.io.IOException; | ||
| 32 | import java.util.Hashtable; | ||
| 33 | import java.util.Map.Entry; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * This is a utility class that can be used by beans that support constrained | ||
| 37 | * properties. It manages a list of listeners and dispatches | ||
| 38 | * {@link PropertyChangeEvent}s to them. You can use an instance of this class | ||
| 39 | * as a member field of your bean and delegate these types of work to it. | ||
| 40 | * The {@link VetoableChangeListener} can be registered for all properties | ||
| 41 | * or for a property specified by name. | ||
| 42 | * <p> | ||
| 43 | * Here is an example of {@code VetoableChangeSupport} usage that follows | ||
| 44 | * the rules and recommendations laid out in the JavaBeans™ specification: | ||
| 45 | * <pre> | ||
| 46 | * public class MyBean { | ||
| 47 | * private final VetoableChangeSupport vcs = new VetoableChangeSupport(this); | ||
| 48 | * | ||
| 49 | * public void addVetoableChangeListener(VetoableChangeListener listener) { | ||
| 50 | * this.vcs.addVetoableChangeListener(listener); | ||
| 51 | * } | ||
| 52 | * | ||
| 53 | * public void removeVetoableChangeListener(VetoableChangeListener listener) { | ||
| 54 | * this.vcs.removeVetoableChangeListener(listener); | ||
| 55 | * } | ||
| 56 | * | ||
| 57 | * private String value; | ||
| 58 | * | ||
| 59 | * public String getValue() { | ||
| 60 | * return this.value; | ||
| 61 | * } | ||
| 62 | * | ||
| 63 | * public void setValue(String newValue) throws PropertyVetoException { | ||
| 64 | * String oldValue = this.value; | ||
| 65 | * this.vcs.fireVetoableChange("value", oldValue, newValue); | ||
| 66 | * this.value = newValue; | ||
| 67 | * } | ||
| 68 | * | ||
| 69 | * [...] | ||
| 70 | * } | ||
| 71 | * </pre> | ||
| 72 | * <p> | ||
| 73 | * A {@code VetoableChangeSupport} instance is thread-safe. | ||
| 74 | * <p> | ||
| 75 | * This class is serializable. When it is serialized it will save | ||
| 76 | * (and restore) any listeners that are themselves serializable. Any | ||
| 77 | * non-serializable listeners will be skipped during serialization. | ||
| 78 | * | ||
| 79 | * @see PropertyChangeSupport | ||
| 80 | */ | ||
| 81 | public class VetoableChangeSupport implements Serializable { | ||
| 82 | private VetoableChangeListenerMap map = new VetoableChangeListenerMap(); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Constructs a <code>VetoableChangeSupport</code> object. | ||
| 86 | * | ||
| 87 | * @param sourceBean The bean to be given as the source for any events. | ||
| 88 | */ | ||
| 89 | public VetoableChangeSupport(Object sourceBean) { | ||
| 90 | if (sourceBean == null) { | ||
| 91 | throw new NullPointerException(); | ||
| 92 | } | ||
| 93 | source = sourceBean; | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Add a VetoableChangeListener to the listener list. | ||
| 98 | * The listener is registered for all properties. | ||
| 99 | * The same listener object may be added more than once, and will be called | ||
| 100 | * as many times as it is added. | ||
| 101 | * If <code>listener</code> is null, no exception is thrown and no action | ||
| 102 | * is taken. | ||
| 103 | * | ||
| 104 | * @param listener The VetoableChangeListener to be added | ||
| 105 | */ | ||
| 106 | public void addVetoableChangeListener(VetoableChangeListener listener) { | ||
| 107 | if (listener == null) { | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | if (listener instanceof VetoableChangeListenerProxy) { | ||
| 111 | VetoableChangeListenerProxy proxy = | ||
| 112 | (VetoableChangeListenerProxy)listener; | ||
| 113 | // Call two argument add method. | ||
| 114 | addVetoableChangeListener(proxy.getPropertyName(), | ||
| 115 | proxy.getListener()); | ||
| 116 | } else { | ||
| 117 | this.map.add(null, listener); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Remove a VetoableChangeListener from the listener list. | ||
| 123 | * This removes a VetoableChangeListener that was registered | ||
| 124 | * for all properties. | ||
| 125 | * If <code>listener</code> was added more than once to the same event | ||
| 126 | * source, it will be notified one less time after being removed. | ||
| 127 | * If <code>listener</code> is null, or was never added, no exception is | ||
| 128 | * thrown and no action is taken. | ||
| 129 | * | ||
| 130 | * @param listener The VetoableChangeListener to be removed | ||
| 131 | */ | ||
| 132 | public void removeVetoableChangeListener(VetoableChangeListener listener) { | ||
| 133 | if (listener == null) { | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | if (listener instanceof VetoableChangeListenerProxy) { | ||
| 137 | VetoableChangeListenerProxy proxy = | ||
| 138 | (VetoableChangeListenerProxy)listener; | ||
| 139 | // Call two argument remove method. | ||
| 140 | removeVetoableChangeListener(proxy.getPropertyName(), | ||
| 141 | proxy.getListener()); | ||
| 142 | } else { | ||
| 143 | this.map.remove(null, listener); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Returns an array of all the listeners that were added to the | ||
| 149 | * VetoableChangeSupport object with addVetoableChangeListener(). | ||
| 150 | * <p> | ||
| 151 | * If some listeners have been added with a named property, then | ||
| 152 | * the returned array will be a mixture of VetoableChangeListeners | ||
| 153 | * and <code>VetoableChangeListenerProxy</code>s. If the calling | ||
| 154 | * method is interested in distinguishing the listeners then it must | ||
| 155 | * test each element to see if it's a | ||
| 156 | * <code>VetoableChangeListenerProxy</code>, perform the cast, and examine | ||
| 157 | * the parameter. | ||
| 158 | * | ||
| 159 | * <pre> | ||
| 160 | * VetoableChangeListener[] listeners = bean.getVetoableChangeListeners(); | ||
| 161 | * for (int i = 0; i < listeners.length; i++) { | ||
| 162 | * if (listeners[i] instanceof VetoableChangeListenerProxy) { | ||
| 163 | * VetoableChangeListenerProxy proxy = | ||
| 164 | * (VetoableChangeListenerProxy)listeners[i]; | ||
| 165 | * if (proxy.getPropertyName().equals("foo")) { | ||
| 166 | * // proxy is a VetoableChangeListener which was associated | ||
| 167 | * // with the property named "foo" | ||
| 168 | * } | ||
| 169 | * } | ||
| 170 | * } | ||
| 171 | *</pre> | ||
| 172 | * | ||
| 173 | * @see VetoableChangeListenerProxy | ||
| 174 | * @return all of the <code>VetoableChangeListeners</code> added or an | ||
| 175 | * empty array if no listeners have been added | ||
| 176 | * @since 1.4 | ||
| 177 | */ | ||
| 178 | public VetoableChangeListener[] getVetoableChangeListeners(){ | ||
| 179 | return this.map.getListeners(); | ||
| 180 | } | ||
| 181 | |||
| 182 | /** | ||
| 183 | * Add a VetoableChangeListener for a specific property. The listener | ||
| 184 | * will be invoked only when a call on fireVetoableChange names that | ||
| 185 | * specific property. | ||
| 186 | * The same listener object may be added more than once. For each | ||
| 187 | * property, the listener will be invoked the number of times it was added | ||
| 188 | * for that property. | ||
| 189 | * If <code>propertyName</code> or <code>listener</code> is null, no | ||
| 190 | * exception is thrown and no action is taken. | ||
| 191 | * | ||
| 192 | * @param propertyName The name of the property to listen on. | ||
| 193 | * @param listener The VetoableChangeListener to be added | ||
| 194 | */ | ||
| 195 | public void addVetoableChangeListener( | ||
| 196 | String propertyName, | ||
| 197 | VetoableChangeListener listener) { | ||
| 198 | if (listener == null || propertyName == null) { | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | listener = this.map.extract(listener); | ||
| 202 | if (listener != null) { | ||
| 203 | this.map.add(propertyName, listener); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * Remove a VetoableChangeListener for a specific property. | ||
| 209 | * If <code>listener</code> was added more than once to the same event | ||
| 210 | * source for the specified property, it will be notified one less time | ||
| 211 | * after being removed. | ||
| 212 | * If <code>propertyName</code> is null, no exception is thrown and no | ||
| 213 | * action is taken. | ||
| 214 | * If <code>listener</code> is null, or was never added for the specified | ||
| 215 | * property, no exception is thrown and no action is taken. | ||
| 216 | * | ||
| 217 | * @param propertyName The name of the property that was listened on. | ||
| 218 | * @param listener The VetoableChangeListener to be removed | ||
| 219 | */ | ||
| 220 | public void removeVetoableChangeListener( | ||
| 221 | String propertyName, | ||
| 222 | VetoableChangeListener listener) { | ||
| 223 | if (listener == null || propertyName == null) { | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | listener = this.map.extract(listener); | ||
| 227 | if (listener != null) { | ||
| 228 | this.map.remove(propertyName, listener); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * Returns an array of all the listeners which have been associated | ||
| 234 | * with the named property. | ||
| 235 | * | ||
| 236 | * @param propertyName The name of the property being listened to | ||
| 237 | * @return all the <code>VetoableChangeListeners</code> associated with | ||
| 238 | * the named property. If no such listeners have been added, | ||
| 239 | * or if <code>propertyName</code> is null, an empty array is | ||
| 240 | * returned. | ||
| 241 | * @since 1.4 | ||
| 242 | */ | ||
| 243 | public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) { | ||
| 244 | return this.map.getListeners(propertyName); | ||
| 245 | } | ||
| 246 | |||
| 247 | /** | ||
| 248 | * Reports a constrained property update to listeners | ||
| 249 | * that have been registered to track updates of | ||
| 250 | * all properties or a property with the specified name. | ||
| 251 | * <p> | ||
| 252 | * Any listener can throw a {@code PropertyVetoException} to veto the update. | ||
| 253 | * If one of the listeners vetoes the update, this method passes | ||
| 254 | * a new "undo" {@code PropertyChangeEvent} that reverts to the old value | ||
| 255 | * to all listeners that already confirmed this update | ||
| 256 | * and throws the {@code PropertyVetoException} again. | ||
| 257 | * <p> | ||
| 258 | * No event is fired if old and new values are equal and non-null. | ||
| 259 | * <p> | ||
| 260 | * This is merely a convenience wrapper around the more general | ||
| 261 | * {@link #fireVetoableChange(PropertyChangeEvent)} method. | ||
| 262 | * | ||
| 263 | * @param propertyName the programmatic name of the property that is about to change | ||
| 264 | * @param oldValue the old value of the property | ||
| 265 | * @param newValue the new value of the property | ||
| 266 | * @throws PropertyVetoException if one of listeners vetoes the property update | ||
| 267 | */ | ||
| 268 | public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) | ||
| 269 | throws PropertyVetoException { | ||
| 270 | if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { | ||
| 271 | fireVetoableChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | /** | ||
| 276 | * Reports an integer constrained property update to listeners | ||
| 277 | * that have been registered to track updates of | ||
| 278 | * all properties or a property with the specified name. | ||
| 279 | * <p> | ||
| 280 | * Any listener can throw a {@code PropertyVetoException} to veto the update. | ||
| 281 | * If one of the listeners vetoes the update, this method passes | ||
| 282 | * a new "undo" {@code PropertyChangeEvent} that reverts to the old value | ||
| 283 | * to all listeners that already confirmed this update | ||
| 284 | * and throws the {@code PropertyVetoException} again. | ||
| 285 | * <p> | ||
| 286 | * No event is fired if old and new values are equal. | ||
| 287 | * <p> | ||
| 288 | * This is merely a convenience wrapper around the more general | ||
| 289 | * {@link #fireVetoableChange(String, Object, Object)} method. | ||
| 290 | * | ||
| 291 | * @param propertyName the programmatic name of the property that is about to change | ||
| 292 | * @param oldValue the old value of the property | ||
| 293 | * @param newValue the new value of the property | ||
| 294 | * @throws PropertyVetoException if one of listeners vetoes the property update | ||
| 295 | */ | ||
| 296 | public void fireVetoableChange(String propertyName, int oldValue, int newValue) | ||
| 297 | throws PropertyVetoException { | ||
| 298 | if (oldValue != newValue) { | ||
| 299 | fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | /** | ||
| 304 | * Reports a boolean constrained property update to listeners | ||
| 305 | * that have been registered to track updates of | ||
| 306 | * all properties or a property with the specified name. | ||
| 307 | * <p> | ||
| 308 | * Any listener can throw a {@code PropertyVetoException} to veto the update. | ||
| 309 | * If one of the listeners vetoes the update, this method passes | ||
| 310 | * a new "undo" {@code PropertyChangeEvent} that reverts to the old value | ||
| 311 | * to all listeners that already confirmed this update | ||
| 312 | * and throws the {@code PropertyVetoException} again. | ||
| 313 | * <p> | ||
| 314 | * No event is fired if old and new values are equal. | ||
| 315 | * <p> | ||
| 316 | * This is merely a convenience wrapper around the more general | ||
| 317 | * {@link #fireVetoableChange(String, Object, Object)} method. | ||
| 318 | * | ||
| 319 | * @param propertyName the programmatic name of the property that is about to change | ||
| 320 | * @param oldValue the old value of the property | ||
| 321 | * @param newValue the new value of the property | ||
| 322 | * @throws PropertyVetoException if one of listeners vetoes the property update | ||
| 323 | */ | ||
| 324 | public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue) | ||
| 325 | throws PropertyVetoException { | ||
| 326 | if (oldValue != newValue) { | ||
| 327 | fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | /** | ||
| 332 | * Fires a property change event to listeners | ||
| 333 | * that have been registered to track updates of | ||
| 334 | * all properties or a property with the specified name. | ||
| 335 | * <p> | ||
| 336 | * Any listener can throw a {@code PropertyVetoException} to veto the update. | ||
| 337 | * If one of the listeners vetoes the update, this method passes | ||
| 338 | * a new "undo" {@code PropertyChangeEvent} that reverts to the old value | ||
| 339 | * to all listeners that already confirmed this update | ||
| 340 | * and throws the {@code PropertyVetoException} again. | ||
| 341 | * <p> | ||
| 342 | * No event is fired if the given event's old and new values are equal and non-null. | ||
| 343 | * | ||
| 344 | * @param event the {@code PropertyChangeEvent} to be fired | ||
| 345 | * @throws PropertyVetoException if one of listeners vetoes the property update | ||
| 346 | */ | ||
| 347 | public void fireVetoableChange(PropertyChangeEvent event) | ||
| 348 | throws PropertyVetoException { | ||
| 349 | Object oldValue = event.getOldValue(); | ||
| 350 | Object newValue = event.getNewValue(); | ||
| 351 | if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { | ||
| 352 | String name = event.getPropertyName(); | ||
| 353 | |||
| 354 | VetoableChangeListener[] common = this.map.get(null); | ||
| 355 | VetoableChangeListener[] named = (name != null) | ||
| 356 | ? this.map.get(name) | ||
| 357 | : null; | ||
| 358 | |||
| 359 | VetoableChangeListener[] listeners; | ||
| 360 | if (common == null) { | ||
| 361 | listeners = named; | ||
| 362 | } | ||
| 363 | else if (named == null) { | ||
| 364 | listeners = common; | ||
| 365 | } | ||
| 366 | else { | ||
| 367 | listeners = new VetoableChangeListener[common.length + named.length]; | ||
| 368 | System.arraycopy(common, 0, listeners, 0, common.length); | ||
| 369 | System.arraycopy(named, 0, listeners, common.length, named.length); | ||
| 370 | } | ||
| 371 | if (listeners != null) { | ||
| 372 | int current = 0; | ||
| 373 | try { | ||
| 374 | while (current < listeners.length) { | ||
| 375 | listeners[current].vetoableChange(event); | ||
| 376 | current++; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | catch (PropertyVetoException veto) { | ||
| 380 | event = new PropertyChangeEvent(this.source, name, newValue, oldValue); | ||
| 381 | for (int i = 0; i < current; i++) { | ||
| 382 | try { | ||
| 383 | listeners[i].vetoableChange(event); | ||
| 384 | } | ||
| 385 | catch (PropertyVetoException exception) { | ||
| 386 | // ignore exceptions that occur during rolling back | ||
| 387 | } | ||
| 388 | } | ||
| 389 | throw veto; // rethrow the veto exception | ||
| 390 | } | ||
| 391 | } | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | /** | ||
| 396 | * Check if there are any listeners for a specific property, including | ||
| 397 | * those registered on all properties. If <code>propertyName</code> | ||
| 398 | * is null, only check for listeners registered on all properties. | ||
| 399 | * | ||
| 400 | * @param propertyName the property name. | ||
| 401 | * @return true if there are one or more listeners for the given property | ||
| 402 | */ | ||
| 403 | public boolean hasListeners(String propertyName) { | ||
| 404 | return this.map.hasListeners(propertyName); | ||
| 405 | } | ||
| 406 | |||
| 407 | /** | ||
| 408 | * @serialData Null terminated list of <code>VetoableChangeListeners</code>. | ||
| 409 | * <p> | ||
| 410 | * At serialization time we skip non-serializable listeners and | ||
| 411 | * only serialize the serializable listeners. | ||
| 412 | */ | ||
| 413 | private void writeObject(ObjectOutputStream s) throws IOException { | ||
| 414 | Hashtable<String, VetoableChangeSupport> children = null; | ||
| 415 | VetoableChangeListener[] listeners = null; | ||
| 416 | synchronized (this.map) { | ||
| 417 | for (Entry<String, VetoableChangeListener[]> entry : this.map.getEntries()) { | ||
| 418 | String property = entry.getKey(); | ||
| 419 | if (property == null) { | ||
| 420 | listeners = entry.getValue(); | ||
| 421 | } else { | ||
| 422 | if (children == null) { | ||
| 423 | children = new Hashtable<String, VetoableChangeSupport>(); | ||
| 424 | } | ||
| 425 | VetoableChangeSupport vcs = new VetoableChangeSupport(this.source); | ||
| 426 | vcs.map.set(null, entry.getValue()); | ||
| 427 | children.put(property, vcs); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | } | ||
| 431 | ObjectOutputStream.PutField fields = s.putFields(); | ||
| 432 | fields.put("children", children); | ||
| 433 | fields.put("source", this.source); | ||
| 434 | fields.put("vetoableChangeSupportSerializedDataVersion", 2); | ||
| 435 | s.writeFields(); | ||
| 436 | |||
| 437 | if (listeners != null) { | ||
| 438 | for (VetoableChangeListener l : listeners) { | ||
| 439 | if (l instanceof Serializable) { | ||
| 440 | s.writeObject(l); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | } | ||
| 444 | s.writeObject(null); | ||
| 445 | } | ||
| 446 | |||
| 447 | private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { | ||
| 448 | this.map = new VetoableChangeListenerMap(); | ||
| 449 | |||
| 450 | ObjectInputStream.GetField fields = s.readFields(); | ||
| 451 | |||
| 452 | Hashtable<String, VetoableChangeSupport> children = (Hashtable<String, VetoableChangeSupport>) fields.get("children", null); | ||
| 453 | this.source = fields.get("source", null); | ||
| 454 | fields.get("vetoableChangeSupportSerializedDataVersion", 2); | ||
| 455 | |||
| 456 | Object listenerOrNull; | ||
| 457 | while (null != (listenerOrNull = s.readObject())) { | ||
| 458 | this.map.add(null, (VetoableChangeListener)listenerOrNull); | ||
| 459 | } | ||
| 460 | if (children != null) { | ||
| 461 | for (Entry<String, VetoableChangeSupport> entry : children.entrySet()) { | ||
| 462 | for (VetoableChangeListener listener : entry.getValue().getVetoableChangeListeners()) { | ||
| 463 | this.map.add(entry.getKey(), listener); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 469 | /** | ||
| 470 | * The object to be provided as the "source" for any generated events. | ||
| 471 | */ | ||
| 472 | private Object source; | ||
| 473 | |||
| 474 | /** | ||
| 475 | * @serialField children Hashtable | ||
| 476 | * @serialField source Object | ||
| 477 | * @serialField vetoableChangeSupportSerializedDataVersion int | ||
| 478 | */ | ||
| 479 | private static final ObjectStreamField[] serialPersistentFields = { | ||
| 480 | new ObjectStreamField("children", Hashtable.class), | ||
| 481 | new ObjectStreamField("source", Object.class), | ||
| 482 | new ObjectStreamField("vetoableChangeSupportSerializedDataVersion", Integer.TYPE) | ||
| 483 | }; | ||
| 484 | |||
| 485 | /** | ||
| 486 | * Serialization version ID, so we're compatible with JDK 1.1 | ||
| 487 | */ | ||
| 488 | static final long serialVersionUID = -5090210921595982017L; | ||
| 489 | |||
| 490 | /** | ||
| 491 | * This is a {@link ChangeListenerMap ChangeListenerMap} implementation | ||
| 492 | * that works with {@link VetoableChangeListener VetoableChangeListener} objects. | ||
| 493 | */ | ||
| 494 | private static final class VetoableChangeListenerMap extends ChangeListenerMap<VetoableChangeListener> { | ||
| 495 | private static final VetoableChangeListener[] EMPTY = {}; | ||
| 496 | |||
| 497 | /** | ||
| 498 | * Creates an array of {@link VetoableChangeListener VetoableChangeListener} objects. | ||
| 499 | * This method uses the same instance of the empty array | ||
| 500 | * when {@code length} equals {@code 0}. | ||
| 501 | * | ||
| 502 | * @param length the array length | ||
| 503 | * @return an array with specified length | ||
| 504 | */ | ||
| 505 | @Override | ||
| 506 | protected VetoableChangeListener[] newArray(int length) { | ||
| 507 | return (0 < length) | ||
| 508 | ? new VetoableChangeListener[length] | ||
| 509 | : EMPTY; | ||
| 510 | } | ||
| 511 | |||
| 512 | /** | ||
| 513 | * Creates a {@link VetoableChangeListenerProxy VetoableChangeListenerProxy} | ||
| 514 | * object for the specified property. | ||
| 515 | * | ||
| 516 | * @param name the name of the property to listen on | ||
| 517 | * @param listener the listener to process events | ||
| 518 | * @return a {@code VetoableChangeListenerProxy} object | ||
| 519 | */ | ||
| 520 | @Override | ||
| 521 | protected VetoableChangeListener newProxy(String name, VetoableChangeListener listener) { | ||
| 522 | return new VetoableChangeListenerProxy(name, listener); | ||
| 523 | } | ||
| 524 | } | ||
| 525 | } | ||
Binary file not shown.
| @@ -0,0 +1,67 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Under some circumstances a bean may be run on servers where a GUI | ||
| 30 | * is not available. This interface can be used to query a bean to | ||
| 31 | * determine whether it absolutely needs a gui, and to advise the | ||
| 32 | * bean whether a GUI is available. | ||
| 33 | * <p> | ||
| 34 | * This interface is for expert developers, and is not needed | ||
| 35 | * for normal simple beans. To avoid confusing end-users we | ||
| 36 | * avoid using getXXX setXXX design patterns for these methods. | ||
| 37 | */ | ||
| 38 | |||
| 39 | public interface Visibility { | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Determines whether this bean needs a GUI. | ||
| 43 | * | ||
| 44 | * @return True if the bean absolutely needs a GUI available in | ||
| 45 | * order to get its work done. | ||
| 46 | */ | ||
| 47 | boolean needsGui(); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * This method instructs the bean that it should not use the Gui. | ||
| 51 | */ | ||
| 52 | void dontUseGui(); | ||
| 53 | |||
| 54 | /** | ||
| 55 | * This method instructs the bean that it is OK to use the Gui. | ||
| 56 | */ | ||
| 57 | void okToUseGui(); | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Determines whether this bean is avoiding using a GUI. | ||
| 61 | * | ||
| 62 | * @return true if the bean is currently avoiding use of the Gui. | ||
| 63 | * e.g. due to a call on dontUseGui(). | ||
| 64 | */ | ||
| 65 | boolean avoidingGui(); | ||
| 66 | |||
| 67 | } | ||
Binary file not shown.
| @@ -0,0 +1,294 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import com.sun.beans.decoder.DocumentHandler; | ||
| 28 | |||
| 29 | import java.io.Closeable; | ||
| 30 | import java.io.InputStream; | ||
| 31 | import java.io.IOException; | ||
| 32 | |||
| 33 | import org.xml.sax.InputSource; | ||
| 34 | import org.xml.sax.helpers.DefaultHandler; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * The <code>XMLDecoder</code> class is used to read XML documents | ||
| 38 | * created using the <code>XMLEncoder</code> and is used just like | ||
| 39 | * the <code>ObjectInputStream</code>. For example, one can use | ||
| 40 | * the following fragment to read the first object defined | ||
| 41 | * in an XML document written by the <code>XMLEncoder</code> | ||
| 42 | * class: | ||
| 43 | * <pre> | ||
| 44 | * XMLDecoder d = new XMLDecoder( | ||
| 45 | * new BufferedInputStream( | ||
| 46 | * new FileInputStream("Test.xml"))); | ||
| 47 | * Object result = d.readObject(); | ||
| 48 | * d.close(); | ||
| 49 | * </pre> | ||
| 50 | * | ||
| 51 | *<p> | ||
| 52 | * For more information you might also want to check out | ||
| 53 | * <a | ||
| 54 | href="http://java.sun.com/products/jfc/tsc/articles/persistence3">Long Term Persistence of JavaBeans Components: XML Schema</a>, | ||
| 55 | * an article in <em>The Swing Connection.</em> | ||
| 56 | * @see XMLEncoder | ||
| 57 | * @see java.io.ObjectInputStream | ||
| 58 | * | ||
| 59 | * @since 1.4 | ||
| 60 | * | ||
| 61 | * @author Philip Milne | ||
| 62 | */ | ||
| 63 | public class XMLDecoder implements AutoCloseable { | ||
| 64 | private final DocumentHandler handler = new DocumentHandler(); | ||
| 65 | private final InputSource input; | ||
| 66 | private Object owner; | ||
| 67 | private Object[] array; | ||
| 68 | private int index; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Creates a new input stream for reading archives | ||
| 72 | * created by the <code>XMLEncoder</code> class. | ||
| 73 | * | ||
| 74 | * @param in The underlying stream. | ||
| 75 | * | ||
| 76 | * @see XMLEncoder#XMLEncoder(java.io.OutputStream) | ||
| 77 | */ | ||
| 78 | public XMLDecoder(InputStream in) { | ||
| 79 | this(in, null); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Creates a new input stream for reading archives | ||
| 84 | * created by the <code>XMLEncoder</code> class. | ||
| 85 | * | ||
| 86 | * @param in The underlying stream. | ||
| 87 | * @param owner The owner of this stream. | ||
| 88 | * | ||
| 89 | */ | ||
| 90 | public XMLDecoder(InputStream in, Object owner) { | ||
| 91 | this(in, owner, null); | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Creates a new input stream for reading archives | ||
| 96 | * created by the <code>XMLEncoder</code> class. | ||
| 97 | * | ||
| 98 | * @param in the underlying stream. | ||
| 99 | * @param owner the owner of this stream. | ||
| 100 | * @param exceptionListener the exception handler for the stream; | ||
| 101 | * if <code>null</code> the default exception listener will be used. | ||
| 102 | */ | ||
| 103 | public XMLDecoder(InputStream in, Object owner, ExceptionListener exceptionListener) { | ||
| 104 | this(in, owner, exceptionListener, null); | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Creates a new input stream for reading archives | ||
| 109 | * created by the <code>XMLEncoder</code> class. | ||
| 110 | * | ||
| 111 | * @param in the underlying stream. <code>null</code> may be passed without | ||
| 112 | * error, though the resulting XMLDecoder will be useless | ||
| 113 | * @param owner the owner of this stream. <code>null</code> is a legal | ||
| 114 | * value | ||
| 115 | * @param exceptionListener the exception handler for the stream, or | ||
| 116 | * <code>null</code> to use the default | ||
| 117 | * @param cl the class loader used for instantiating objects. | ||
| 118 | * <code>null</code> indicates that the default class loader should | ||
| 119 | * be used | ||
| 120 | * @since 1.5 | ||
| 121 | */ | ||
| 122 | public XMLDecoder(InputStream in, Object owner, | ||
| 123 | ExceptionListener exceptionListener, ClassLoader cl) { | ||
| 124 | this(new InputSource(in), owner, exceptionListener, cl); | ||
| 125 | } | ||
| 126 | |||
| 127 | |||
| 128 | /** | ||
| 129 | * Creates a new decoder to parse XML archives | ||
| 130 | * created by the {@code XMLEncoder} class. | ||
| 131 | * If the input source {@code is} is {@code null}, | ||
| 132 | * no exception is thrown and no parsing is performed. | ||
| 133 | * This behavior is similar to behavior of other constructors | ||
| 134 | * that use {@code InputStream} as a parameter. | ||
| 135 | * | ||
| 136 | * @param is the input source to parse | ||
| 137 | * | ||
| 138 | * @since 1.7 | ||
| 139 | */ | ||
| 140 | public XMLDecoder(InputSource is) { | ||
| 141 | this(is, null, null, null); | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Creates a new decoder to parse XML archives | ||
| 146 | * created by the {@code XMLEncoder} class. | ||
| 147 | * | ||
| 148 | * @param is the input source to parse | ||
| 149 | * @param owner the owner of this decoder | ||
| 150 | * @param el the exception handler for the parser, | ||
| 151 | * or {@code null} to use the default exception handler | ||
| 152 | * @param cl the class loader used for instantiating objects, | ||
| 153 | * or {@code null} to use the default class loader | ||
| 154 | * | ||
| 155 | * @since 1.7 | ||
| 156 | */ | ||
| 157 | private XMLDecoder(InputSource is, Object owner, ExceptionListener el, ClassLoader cl) { | ||
| 158 | this.input = is; | ||
| 159 | this.owner = owner; | ||
| 160 | setExceptionListener(el); | ||
| 161 | this.handler.setClassLoader(cl); | ||
| 162 | this.handler.setOwner(this); | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * This method closes the input stream associated | ||
| 167 | * with this stream. | ||
| 168 | */ | ||
| 169 | public void close() { | ||
| 170 | if (parsingComplete()) { | ||
| 171 | close(this.input.getCharacterStream()); | ||
| 172 | close(this.input.getByteStream()); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | private void close(Closeable in) { | ||
| 177 | if (in != null) { | ||
| 178 | try { | ||
| 179 | in.close(); | ||
| 180 | } | ||
| 181 | catch (IOException e) { | ||
| 182 | getExceptionListener().exceptionThrown(e); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | private boolean parsingComplete() { | ||
| 188 | if (this.input == null) { | ||
| 189 | return false; | ||
| 190 | } | ||
| 191 | if (this.array == null) { | ||
| 192 | this.handler.parse(this.input); | ||
| 193 | this.array = this.handler.getObjects(); | ||
| 194 | } | ||
| 195 | return true; | ||
| 196 | } | ||
| 197 | |||
| 198 | /** | ||
| 199 | * Sets the exception handler for this stream to <code>exceptionListener</code>. | ||
| 200 | * The exception handler is notified when this stream catches recoverable | ||
| 201 | * exceptions. | ||
| 202 | * | ||
| 203 | * @param exceptionListener The exception handler for this stream; | ||
| 204 | * if <code>null</code> the default exception listener will be used. | ||
| 205 | * | ||
| 206 | * @see #getExceptionListener | ||
| 207 | */ | ||
| 208 | public void setExceptionListener(ExceptionListener exceptionListener) { | ||
| 209 | if (exceptionListener == null) { | ||
| 210 | exceptionListener = Statement.defaultExceptionListener; | ||
| 211 | } | ||
| 212 | this.handler.setExceptionListener(exceptionListener); | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * Gets the exception handler for this stream. | ||
| 217 | * | ||
| 218 | * @return The exception handler for this stream. | ||
| 219 | * Will return the default exception listener if this has not explicitly been set. | ||
| 220 | * | ||
| 221 | * @see #setExceptionListener | ||
| 222 | */ | ||
| 223 | public ExceptionListener getExceptionListener() { | ||
| 224 | return this.handler.getExceptionListener(); | ||
| 225 | } | ||
| 226 | |||
| 227 | /** | ||
| 228 | * Reads the next object from the underlying input stream. | ||
| 229 | * | ||
| 230 | * @return the next object read | ||
| 231 | * | ||
| 232 | * @throws ArrayIndexOutOfBoundsException if the stream contains no objects | ||
| 233 | * (or no more objects) | ||
| 234 | * | ||
| 235 | * @see XMLEncoder#writeObject | ||
| 236 | */ | ||
| 237 | public Object readObject() { | ||
| 238 | return (parsingComplete()) | ||
| 239 | ? this.array[this.index++] | ||
| 240 | : null; | ||
| 241 | } | ||
| 242 | |||
| 243 | /** | ||
| 244 | * Sets the owner of this decoder to <code>owner</code>. | ||
| 245 | * | ||
| 246 | * @param owner The owner of this decoder. | ||
| 247 | * | ||
| 248 | * @see #getOwner | ||
| 249 | */ | ||
| 250 | public void setOwner(Object owner) { | ||
| 251 | this.owner = owner; | ||
| 252 | } | ||
| 253 | |||
| 254 | /** | ||
| 255 | * Gets the owner of this decoder. | ||
| 256 | * | ||
| 257 | * @return The owner of this decoder. | ||
| 258 | * | ||
| 259 | * @see #setOwner | ||
| 260 | */ | ||
| 261 | public Object getOwner() { | ||
| 262 | return owner; | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * Creates a new handler for SAX parser | ||
| 267 | * that can be used to parse embedded XML archives | ||
| 268 | * created by the {@code XMLEncoder} class. | ||
| 269 | * | ||
| 270 | * The {@code owner} should be used if parsed XML document contains | ||
| 271 | * the method call within context of the <java> element. | ||
| 272 | * The {@code null} value may cause illegal parsing in such case. | ||
| 273 | * The same problem may occur, if the {@code owner} class | ||
| 274 | * does not contain expected method to call. See details <a | ||
| 275 | * href="http://java.sun.com/products/jfc/tsc/articles/persistence3/">here</a>. | ||
| 276 | * | ||
| 277 | * @param owner the owner of the default handler | ||
| 278 | * that can be used as a value of <java> element | ||
| 279 | * @param el the exception handler for the parser, | ||
| 280 | * or {@code null} to use the default exception handler | ||
| 281 | * @param cl the class loader used for instantiating objects, | ||
| 282 | * or {@code null} to use the default class loader | ||
| 283 | * @return an instance of {@code DefaultHandler} for SAX parser | ||
| 284 | * | ||
| 285 | * @since 1.7 | ||
| 286 | */ | ||
| 287 | public static DefaultHandler createHandler(Object owner, ExceptionListener el, ClassLoader cl) { | ||
| 288 | DocumentHandler handler = new DocumentHandler(); | ||
| 289 | handler.setOwner(owner); | ||
| 290 | handler.setExceptionListener(el); | ||
| 291 | handler.setClassLoader(cl); | ||
| 292 | return handler; | ||
| 293 | } | ||
| 294 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,774 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans; | ||
| 26 | |||
| 27 | import java.io.*; | ||
| 28 | import java.util.*; | ||
| 29 | import java.lang.reflect.*; | ||
| 30 | import java.nio.charset.Charset; | ||
| 31 | import java.nio.charset.CharsetEncoder; | ||
| 32 | import java.nio.charset.IllegalCharsetNameException; | ||
| 33 | import java.nio.charset.UnsupportedCharsetException; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * The <code>XMLEncoder</code> class is a complementary alternative to | ||
| 37 | * the <code>ObjectOutputStream</code> and can used to generate | ||
| 38 | * a textual representation of a <em>JavaBean</em> in the same | ||
| 39 | * way that the <code>ObjectOutputStream</code> can | ||
| 40 | * be used to create binary representation of <code>Serializable</code> | ||
| 41 | * objects. For example, the following fragment can be used to create | ||
| 42 | * a textual representation the supplied <em>JavaBean</em> | ||
| 43 | * and all its properties: | ||
| 44 | * <pre> | ||
| 45 | * XMLEncoder e = new XMLEncoder( | ||
| 46 | * new BufferedOutputStream( | ||
| 47 | * new FileOutputStream("Test.xml"))); | ||
| 48 | * e.writeObject(new JButton("Hello, world")); | ||
| 49 | * e.close(); | ||
| 50 | * </pre> | ||
| 51 | * Despite the similarity of their APIs, the <code>XMLEncoder</code> | ||
| 52 | * class is exclusively designed for the purpose of archiving graphs | ||
| 53 | * of <em>JavaBean</em>s as textual representations of their public | ||
| 54 | * properties. Like Java source files, documents written this way | ||
| 55 | * have a natural immunity to changes in the implementations of the classes | ||
| 56 | * involved. The <code>ObjectOutputStream</code> continues to be recommended | ||
| 57 | * for interprocess communication and general purpose serialization. | ||
| 58 | * <p> | ||
| 59 | * The <code>XMLEncoder</code> class provides a default denotation for | ||
| 60 | * <em>JavaBean</em>s in which they are represented as XML documents | ||
| 61 | * complying with version 1.0 of the XML specification and the | ||
| 62 | * UTF-8 character encoding of the Unicode/ISO 10646 character set. | ||
| 63 | * The XML documents produced by the <code>XMLEncoder</code> class are: | ||
| 64 | * <ul> | ||
| 65 | * <li> | ||
| 66 | * <em>Portable and version resilient</em>: they have no dependencies | ||
| 67 | * on the private implementation of any class and so, like Java source | ||
| 68 | * files, they may be exchanged between environments which may have | ||
| 69 | * different versions of some of the classes and between VMs from | ||
| 70 | * different vendors. | ||
| 71 | * <li> | ||
| 72 | * <em>Structurally compact</em>: The <code>XMLEncoder</code> class | ||
| 73 | * uses a <em>redundancy elimination</em> algorithm internally so that the | ||
| 74 | * default values of a Bean's properties are not written to the stream. | ||
| 75 | * <li> | ||
| 76 | * <em>Fault tolerant</em>: Non-structural errors in the file, | ||
| 77 | * caused either by damage to the file or by API changes | ||
| 78 | * made to classes in an archive remain localized | ||
| 79 | * so that a reader can report the error and continue to load the parts | ||
| 80 | * of the document which were not affected by the error. | ||
| 81 | * </ul> | ||
| 82 | * <p> | ||
| 83 | * Below is an example of an XML archive containing | ||
| 84 | * some user interface components from the <em>swing</em> toolkit: | ||
| 85 | * <pre> | ||
| 86 | * <?xml version="1.0" encoding="UTF-8"?> | ||
| 87 | * <java version="1.0" class="java.beans.XMLDecoder"> | ||
| 88 | * <object class="javax.swing.JFrame"> | ||
| 89 | * <void property="name"> | ||
| 90 | * <string>frame1</string> | ||
| 91 | * </void> | ||
| 92 | * <void property="bounds"> | ||
| 93 | * <object class="java.awt.Rectangle"> | ||
| 94 | * <int>0</int> | ||
| 95 | * <int>0</int> | ||
| 96 | * <int>200</int> | ||
| 97 | * <int>200</int> | ||
| 98 | * </object> | ||
| 99 | * </void> | ||
| 100 | * <void property="contentPane"> | ||
| 101 | * <void method="add"> | ||
| 102 | * <object class="javax.swing.JButton"> | ||
| 103 | * <void property="label"> | ||
| 104 | * <string>Hello</string> | ||
| 105 | * </void> | ||
| 106 | * </object> | ||
| 107 | * </void> | ||
| 108 | * </void> | ||
| 109 | * <void property="visible"> | ||
| 110 | * <boolean>true</boolean> | ||
| 111 | * </void> | ||
| 112 | * </object> | ||
| 113 | * </java> | ||
| 114 | * </pre> | ||
| 115 | * The XML syntax uses the following conventions: | ||
| 116 | * <ul> | ||
| 117 | * <li> | ||
| 118 | * Each element represents a method call. | ||
| 119 | * <li> | ||
| 120 | * The "object" tag denotes an <em>expression</em> whose value is | ||
| 121 | * to be used as the argument to the enclosing element. | ||
| 122 | * <li> | ||
| 123 | * The "void" tag denotes a <em>statement</em> which will | ||
| 124 | * be executed, but whose result will not be used as an | ||
| 125 | * argument to the enclosing method. | ||
| 126 | * <li> | ||
| 127 | * Elements which contain elements use those elements as arguments, | ||
| 128 | * unless they have the tag: "void". | ||
| 129 | * <li> | ||
| 130 | * The name of the method is denoted by the "method" attribute. | ||
| 131 | * <li> | ||
| 132 | * XML's standard "id" and "idref" attributes are used to make | ||
| 133 | * references to previous expressions - so as to deal with | ||
| 134 | * circularities in the object graph. | ||
| 135 | * <li> | ||
| 136 | * The "class" attribute is used to specify the target of a static | ||
| 137 | * method or constructor explicitly; its value being the fully | ||
| 138 | * qualified name of the class. | ||
| 139 | * <li> | ||
| 140 | * Elements with the "void" tag are executed using | ||
| 141 | * the outer context as the target if no target is defined | ||
| 142 | * by a "class" attribute. | ||
| 143 | * <li> | ||
| 144 | * Java's String class is treated specially and is | ||
| 145 | * written <string>Hello, world</string> where | ||
| 146 | * the characters of the string are converted to bytes | ||
| 147 | * using the UTF-8 character encoding. | ||
| 148 | * </ul> | ||
| 149 | * <p> | ||
| 150 | * Although all object graphs may be written using just these three | ||
| 151 | * tags, the following definitions are included so that common | ||
| 152 | * data structures can be expressed more concisely: | ||
| 153 | * <p> | ||
| 154 | * <ul> | ||
| 155 | * <li> | ||
| 156 | * The default method name is "new". | ||
| 157 | * <li> | ||
| 158 | * A reference to a java class is written in the form | ||
| 159 | * <class>javax.swing.JButton</class>. | ||
| 160 | * <li> | ||
| 161 | * Instances of the wrapper classes for Java's primitive types are written | ||
| 162 | * using the name of the primitive type as the tag. For example, an | ||
| 163 | * instance of the <code>Integer</code> class could be written: | ||
| 164 | * <int>123</int>. Note that the <code>XMLEncoder</code> class | ||
| 165 | * uses Java's reflection package in which the conversion between | ||
| 166 | * Java's primitive types and their associated "wrapper classes" | ||
| 167 | * is handled internally. The API for the <code>XMLEncoder</code> class | ||
| 168 | * itself deals only with <code>Object</code>s. | ||
| 169 | * <li> | ||
| 170 | * In an element representing a nullary method whose name | ||
| 171 | * starts with "get", the "method" attribute is replaced | ||
| 172 | * with a "property" attribute whose value is given by removing | ||
| 173 | * the "get" prefix and decapitalizing the result. | ||
| 174 | * <li> | ||
| 175 | * In an element representing a monadic method whose name | ||
| 176 | * starts with "set", the "method" attribute is replaced | ||
| 177 | * with a "property" attribute whose value is given by removing | ||
| 178 | * the "set" prefix and decapitalizing the result. | ||
| 179 | * <li> | ||
| 180 | * In an element representing a method named "get" taking one | ||
| 181 | * integer argument, the "method" attribute is replaced | ||
| 182 | * with an "index" attribute whose value the value of the | ||
| 183 | * first argument. | ||
| 184 | * <li> | ||
| 185 | * In an element representing a method named "set" taking two arguments, | ||
| 186 | * the first of which is an integer, the "method" attribute is replaced | ||
| 187 | * with an "index" attribute whose value the value of the | ||
| 188 | * first argument. | ||
| 189 | * <li> | ||
| 190 | * A reference to an array is written using the "array" | ||
| 191 | * tag. The "class" and "length" attributes specify the | ||
| 192 | * sub-type of the array and its length respectively. | ||
| 193 | * </ul> | ||
| 194 | * | ||
| 195 | *<p> | ||
| 196 | * For more information you might also want to check out | ||
| 197 | * <a | ||
| 198 | href="http://java.sun.com/products/jfc/tsc/articles/persistence4">Using XMLEncoder</a>, | ||
| 199 | * an article in <em>The Swing Connection.</em> | ||
| 200 | * @see XMLDecoder | ||
| 201 | * @see java.io.ObjectOutputStream | ||
| 202 | * | ||
| 203 | * @since 1.4 | ||
| 204 | * | ||
| 205 | * @author Philip Milne | ||
| 206 | */ | ||
| 207 | public class XMLEncoder extends Encoder implements AutoCloseable { | ||
| 208 | |||
| 209 | private final CharsetEncoder encoder; | ||
| 210 | private final String charset; | ||
| 211 | private final boolean declaration; | ||
| 212 | |||
| 213 | private OutputStreamWriter out; | ||
| 214 | private Object owner; | ||
| 215 | private int indentation = 0; | ||
| 216 | private boolean internal = false; | ||
| 217 | private Map<Object, ValueData> valueToExpression; | ||
| 218 | private Map<Object, List<Statement>> targetToStatementList; | ||
| 219 | private boolean preambleWritten = false; | ||
| 220 | private NameGenerator nameGenerator; | ||
| 221 | |||
| 222 | private class ValueData { | ||
| 223 | public int refs = 0; | ||
| 224 | public boolean marked = false; // Marked -> refs > 0 unless ref was a target. | ||
| 225 | public String name = null; | ||
| 226 | public Expression exp = null; | ||
| 227 | } | ||
| 228 | |||
| 229 | /** | ||
| 230 | * Creates a new XML encoder to write out <em>JavaBeans</em> | ||
| 231 | * to the stream <code>out</code> using an XML encoding. | ||
| 232 | * | ||
| 233 | * @param out the stream to which the XML representation of | ||
| 234 | * the objects will be written | ||
| 235 | * | ||
| 236 | * @throws IllegalArgumentException | ||
| 237 | * if <code>out</code> is <code>null</code> | ||
| 238 | * | ||
| 239 | * @see XMLDecoder#XMLDecoder(InputStream) | ||
| 240 | */ | ||
| 241 | public XMLEncoder(OutputStream out) { | ||
| 242 | this(out, "UTF-8", true, 0); | ||
| 243 | } | ||
| 244 | |||
| 245 | /** | ||
| 246 | * Creates a new XML encoder to write out <em>JavaBeans</em> | ||
| 247 | * to the stream <code>out</code> using the given <code>charset</code> | ||
| 248 | * starting from the given <code>indentation</code>. | ||
| 249 | * | ||
| 250 | * @param out the stream to which the XML representation of | ||
| 251 | * the objects will be written | ||
| 252 | * @param charset the name of the requested charset; | ||
| 253 | * may be either a canonical name or an alias | ||
| 254 | * @param declaration whether the XML declaration should be generated; | ||
| 255 | * set this to <code>false</code> | ||
| 256 | * when embedding the contents in another XML document | ||
| 257 | * @param indentation the number of space characters to indent the entire XML document by | ||
| 258 | * | ||
| 259 | * @throws IllegalArgumentException | ||
| 260 | * if <code>out</code> or <code>charset</code> is <code>null</code>, | ||
| 261 | * or if <code>indentation</code> is less than 0 | ||
| 262 | * | ||
| 263 | * @throws IllegalCharsetNameException | ||
| 264 | * if <code>charset</code> name is illegal | ||
| 265 | * | ||
| 266 | * @throws UnsupportedCharsetException | ||
| 267 | * if no support for the named charset is available | ||
| 268 | * in this instance of the Java virtual machine | ||
| 269 | * | ||
| 270 | * @throws UnsupportedOperationException | ||
| 271 | * if loaded charset does not support encoding | ||
| 272 | * | ||
| 273 | * @see Charset#forName(String) | ||
| 274 | * | ||
| 275 | * @since 1.7 | ||
| 276 | */ | ||
| 277 | public XMLEncoder(OutputStream out, String charset, boolean declaration, int indentation) { | ||
| 278 | if (out == null) { | ||
| 279 | throw new IllegalArgumentException("the output stream cannot be null"); | ||
| 280 | } | ||
| 281 | if (indentation < 0) { | ||
| 282 | throw new IllegalArgumentException("the indentation must be >= 0"); | ||
| 283 | } | ||
| 284 | Charset cs = Charset.forName(charset); | ||
| 285 | this.encoder = cs.newEncoder(); | ||
| 286 | this.charset = charset; | ||
| 287 | this.declaration = declaration; | ||
| 288 | this.indentation = indentation; | ||
| 289 | this.out = new OutputStreamWriter(out, cs.newEncoder()); | ||
| 290 | valueToExpression = new IdentityHashMap<Object, ValueData>(); | ||
| 291 | targetToStatementList = new IdentityHashMap<Object, List<Statement>>(); | ||
| 292 | nameGenerator = new NameGenerator(); | ||
| 293 | } | ||
| 294 | |||
| 295 | /** | ||
| 296 | * Sets the owner of this encoder to <code>owner</code>. | ||
| 297 | * | ||
| 298 | * @param owner The owner of this encoder. | ||
| 299 | * | ||
| 300 | * @see #getOwner | ||
| 301 | */ | ||
| 302 | public void setOwner(Object owner) { | ||
| 303 | this.owner = owner; | ||
| 304 | writeExpression(new Expression(this, "getOwner", new Object[0])); | ||
| 305 | } | ||
| 306 | |||
| 307 | /** | ||
| 308 | * Gets the owner of this encoder. | ||
| 309 | * | ||
| 310 | * @return The owner of this encoder. | ||
| 311 | * | ||
| 312 | * @see #setOwner | ||
| 313 | */ | ||
| 314 | public Object getOwner() { | ||
| 315 | return owner; | ||
| 316 | } | ||
| 317 | |||
| 318 | /** | ||
| 319 | * Write an XML representation of the specified object to the output. | ||
| 320 | * | ||
| 321 | * @param o The object to be written to the stream. | ||
| 322 | * | ||
| 323 | * @see XMLDecoder#readObject | ||
| 324 | */ | ||
| 325 | public void writeObject(Object o) { | ||
| 326 | if (internal) { | ||
| 327 | super.writeObject(o); | ||
| 328 | } | ||
| 329 | else { | ||
| 330 | writeStatement(new Statement(this, "writeObject", new Object[]{o})); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | private List<Statement> statementList(Object target) { | ||
| 335 | List<Statement> list = targetToStatementList.get(target); | ||
| 336 | if (list == null) { | ||
| 337 | list = new ArrayList<Statement>(); | ||
| 338 | targetToStatementList.put(target, list); | ||
| 339 | } | ||
| 340 | return list; | ||
| 341 | } | ||
| 342 | |||
| 343 | |||
| 344 | private void mark(Object o, boolean isArgument) { | ||
| 345 | if (o == null || o == this) { | ||
| 346 | return; | ||
| 347 | } | ||
| 348 | ValueData d = getValueData(o); | ||
| 349 | Expression exp = d.exp; | ||
| 350 | // Do not mark liternal strings. Other strings, which might, | ||
| 351 | // for example, come from resource bundles should still be marked. | ||
| 352 | if (o.getClass() == String.class && exp == null) { | ||
| 353 | return; | ||
| 354 | } | ||
| 355 | |||
| 356 | // Bump the reference counts of all arguments | ||
| 357 | if (isArgument) { | ||
| 358 | d.refs++; | ||
| 359 | } | ||
| 360 | if (d.marked) { | ||
| 361 | return; | ||
| 362 | } | ||
| 363 | d.marked = true; | ||
| 364 | Object target = exp.getTarget(); | ||
| 365 | mark(exp); | ||
| 366 | if (!(target instanceof Class)) { | ||
| 367 | statementList(target).add(exp); | ||
| 368 | // Pending: Why does the reference count need to | ||
| 369 | // be incremented here? | ||
| 370 | d.refs++; | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | private void mark(Statement stm) { | ||
| 375 | Object[] args = stm.getArguments(); | ||
| 376 | for (int i = 0; i < args.length; i++) { | ||
| 377 | Object arg = args[i]; | ||
| 378 | mark(arg, true); | ||
| 379 | } | ||
| 380 | mark(stm.getTarget(), false); | ||
| 381 | } | ||
| 382 | |||
| 383 | |||
| 384 | /** | ||
| 385 | * Records the Statement so that the Encoder will | ||
| 386 | * produce the actual output when the stream is flushed. | ||
| 387 | * <P> | ||
| 388 | * This method should only be invoked within the context | ||
| 389 | * of initializing a persistence delegate. | ||
| 390 | * | ||
| 391 | * @param oldStm The statement that will be written | ||
| 392 | * to the stream. | ||
| 393 | * @see java.beans.PersistenceDelegate#initialize | ||
| 394 | */ | ||
| 395 | public void writeStatement(Statement oldStm) { | ||
| 396 | // System.out.println("XMLEncoder::writeStatement: " + oldStm); | ||
| 397 | boolean internal = this.internal; | ||
| 398 | this.internal = true; | ||
| 399 | try { | ||
| 400 | super.writeStatement(oldStm); | ||
| 401 | /* | ||
| 402 | Note we must do the mark first as we may | ||
| 403 | require the results of previous values in | ||
| 404 | this context for this statement. | ||
| 405 | Test case is: | ||
| 406 | os.setOwner(this); | ||
| 407 | os.writeObject(this); | ||
| 408 | */ | ||
| 409 | mark(oldStm); | ||
| 410 | Object target = oldStm.getTarget(); | ||
| 411 | if (target instanceof Field) { | ||
| 412 | String method = oldStm.getMethodName(); | ||
| 413 | Object[] args = oldStm.getArguments(); | ||
| 414 | if ((method == null) || (args == null)) { | ||
| 415 | } | ||
| 416 | else if (method.equals("get") && (args.length == 1)) { | ||
| 417 | target = args[0]; | ||
| 418 | } | ||
| 419 | else if (method.equals("set") && (args.length == 2)) { | ||
| 420 | target = args[0]; | ||
| 421 | } | ||
| 422 | } | ||
| 423 | statementList(target).add(oldStm); | ||
| 424 | } | ||
| 425 | catch (Exception e) { | ||
| 426 | getExceptionListener().exceptionThrown(new Exception("XMLEncoder: discarding statement " + oldStm, e)); | ||
| 427 | } | ||
| 428 | this.internal = internal; | ||
| 429 | } | ||
| 430 | |||
| 431 | |||
| 432 | /** | ||
| 433 | * Records the Expression so that the Encoder will | ||
| 434 | * produce the actual output when the stream is flushed. | ||
| 435 | * <P> | ||
| 436 | * This method should only be invoked within the context of | ||
| 437 | * initializing a persistence delegate or setting up an encoder to | ||
| 438 | * read from a resource bundle. | ||
| 439 | * <P> | ||
| 440 | * For more information about using resource bundles with the | ||
| 441 | * XMLEncoder, see | ||
| 442 | * http://java.sun.com/products/jfc/tsc/articles/persistence4/#i18n | ||
| 443 | * | ||
| 444 | * @param oldExp The expression that will be written | ||
| 445 | * to the stream. | ||
| 446 | * @see java.beans.PersistenceDelegate#initialize | ||
| 447 | */ | ||
| 448 | public void writeExpression(Expression oldExp) { | ||
| 449 | boolean internal = this.internal; | ||
| 450 | this.internal = true; | ||
| 451 | Object oldValue = getValue(oldExp); | ||
| 452 | if (get(oldValue) == null || (oldValue instanceof String && !internal)) { | ||
| 453 | getValueData(oldValue).exp = oldExp; | ||
| 454 | super.writeExpression(oldExp); | ||
| 455 | } | ||
| 456 | this.internal = internal; | ||
| 457 | } | ||
| 458 | |||
| 459 | /** | ||
| 460 | * This method writes out the preamble associated with the | ||
| 461 | * XML encoding if it has not been written already and | ||
| 462 | * then writes out all of the values that been | ||
| 463 | * written to the stream since the last time <code>flush</code> | ||
| 464 | * was called. After flushing, all internal references to the | ||
| 465 | * values that were written to this stream are cleared. | ||
| 466 | */ | ||
| 467 | public void flush() { | ||
| 468 | if (!preambleWritten) { // Don't do this in constructor - it throws ... pending. | ||
| 469 | if (this.declaration) { | ||
| 470 | writeln("<?xml version=" + quote("1.0") + | ||
| 471 | " encoding=" + quote(this.charset) + "?>"); | ||
| 472 | } | ||
| 473 | writeln("<java version=" + quote(System.getProperty("java.version")) + | ||
| 474 | " class=" + quote(XMLDecoder.class.getName()) + ">"); | ||
| 475 | preambleWritten = true; | ||
| 476 | } | ||
| 477 | indentation++; | ||
| 478 | List<Statement> statements = statementList(this); | ||
| 479 | while (!statements.isEmpty()) { | ||
| 480 | Statement s = statements.remove(0); | ||
| 481 | if ("writeObject".equals(s.getMethodName())) { | ||
| 482 | outputValue(s.getArguments()[0], this, true); | ||
| 483 | } | ||
| 484 | else { | ||
| 485 | outputStatement(s, this, false); | ||
| 486 | } | ||
| 487 | } | ||
| 488 | indentation--; | ||
| 489 | |||
| 490 | try { | ||
| 491 | out.flush(); | ||
| 492 | } | ||
| 493 | catch (IOException e) { | ||
| 494 | getExceptionListener().exceptionThrown(e); | ||
| 495 | } | ||
| 496 | clear(); | ||
| 497 | } | ||
| 498 | |||
| 499 | void clear() { | ||
| 500 | super.clear(); | ||
| 501 | nameGenerator.clear(); | ||
| 502 | valueToExpression.clear(); | ||
| 503 | targetToStatementList.clear(); | ||
| 504 | } | ||
| 505 | |||
| 506 | |||
| 507 | /** | ||
| 508 | * This method calls <code>flush</code>, writes the closing | ||
| 509 | * postamble and then closes the output stream associated | ||
| 510 | * with this stream. | ||
| 511 | */ | ||
| 512 | public void close() { | ||
| 513 | flush(); | ||
| 514 | writeln("</java>"); | ||
| 515 | try { | ||
| 516 | out.close(); | ||
| 517 | } | ||
| 518 | catch (IOException e) { | ||
| 519 | getExceptionListener().exceptionThrown(e); | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | private String quote(String s) { | ||
| 524 | return "\"" + s + "\""; | ||
| 525 | } | ||
| 526 | |||
| 527 | private ValueData getValueData(Object o) { | ||
| 528 | ValueData d = valueToExpression.get(o); | ||
| 529 | if (d == null) { | ||
| 530 | d = new ValueData(); | ||
| 531 | valueToExpression.put(o, d); | ||
| 532 | } | ||
| 533 | return d; | ||
| 534 | } | ||
| 535 | |||
| 536 | /** | ||
| 537 | * Returns <code>true</code> if the argument, | ||
| 538 | * a Unicode code point, is valid in XML documents. | ||
| 539 | * Unicode characters fit into the low sixteen bits of a Unicode code point, | ||
| 540 | * and pairs of Unicode <em>surrogate characters</em> can be combined | ||
| 541 | * to encode Unicode code point in documents containing only Unicode. | ||
| 542 | * (The <code>char</code> datatype in the Java Programming Language | ||
| 543 | * represents Unicode characters, including unpaired surrogates.) | ||
| 544 | * <par> | ||
| 545 | * [2] Char ::= #x0009 | #x000A | #x000D | ||
| 546 | * | [#x0020-#xD7FF] | ||
| 547 | * | [#xE000-#xFFFD] | ||
| 548 | * | [#x10000-#x10ffff] | ||
| 549 | * </par> | ||
| 550 | * | ||
| 551 | * @param code the 32-bit Unicode code point being tested | ||
| 552 | * @return <code>true</code> if the Unicode code point is valid, | ||
| 553 | * <code>false</code> otherwise | ||
| 554 | */ | ||
| 555 | private static boolean isValidCharCode(int code) { | ||
| 556 | return (0x0020 <= code && code <= 0xD7FF) | ||
| 557 | || (0x000A == code) | ||
| 558 | || (0x0009 == code) | ||
| 559 | || (0x000D == code) | ||
| 560 | || (0xE000 <= code && code <= 0xFFFD) | ||
| 561 | || (0x10000 <= code && code <= 0x10ffff); | ||
| 562 | } | ||
| 563 | |||
| 564 | private void writeln(String exp) { | ||
| 565 | try { | ||
| 566 | StringBuilder sb = new StringBuilder(); | ||
| 567 | for(int i = 0; i < indentation; i++) { | ||
| 568 | sb.append(' '); | ||
| 569 | } | ||
| 570 | sb.append(exp); | ||
| 571 | sb.append('\n'); | ||
| 572 | this.out.write(sb.toString()); | ||
| 573 | } | ||
| 574 | catch (IOException e) { | ||
| 575 | getExceptionListener().exceptionThrown(e); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | private void outputValue(Object value, Object outer, boolean isArgument) { | ||
| 580 | if (value == null) { | ||
| 581 | writeln("<null/>"); | ||
| 582 | return; | ||
| 583 | } | ||
| 584 | |||
| 585 | if (value instanceof Class) { | ||
| 586 | writeln("<class>" + ((Class)value).getName() + "</class>"); | ||
| 587 | return; | ||
| 588 | } | ||
| 589 | |||
| 590 | ValueData d = getValueData(value); | ||
| 591 | if (d.exp != null) { | ||
| 592 | Object target = d.exp.getTarget(); | ||
| 593 | String methodName = d.exp.getMethodName(); | ||
| 594 | |||
| 595 | if (target == null || methodName == null) { | ||
| 596 | throw new NullPointerException((target == null ? "target" : | ||
| 597 | "methodName") + " should not be null"); | ||
| 598 | } | ||
| 599 | |||
| 600 | if (target instanceof Field && methodName.equals("get")) { | ||
| 601 | Field f = (Field)target; | ||
| 602 | writeln("<object class=" + quote(f.getDeclaringClass().getName()) + | ||
| 603 | " field=" + quote(f.getName()) + "/>"); | ||
| 604 | return; | ||
| 605 | } | ||
| 606 | |||
| 607 | Class primitiveType = ReflectionUtils.primitiveTypeFor(value.getClass()); | ||
| 608 | if (primitiveType != null && target == value.getClass() && | ||
| 609 | methodName.equals("new")) { | ||
| 610 | String primitiveTypeName = primitiveType.getName(); | ||
| 611 | // Make sure that character types are quoted correctly. | ||
| 612 | if (primitiveType == Character.TYPE) { | ||
| 613 | char code = ((Character) value).charValue(); | ||
| 614 | if (!isValidCharCode(code)) { | ||
| 615 | writeln(createString(code)); | ||
| 616 | return; | ||
| 617 | } | ||
| 618 | value = quoteCharCode(code); | ||
| 619 | if (value == null) { | ||
| 620 | value = Character.valueOf(code); | ||
| 621 | } | ||
| 622 | } | ||
| 623 | writeln("<" + primitiveTypeName + ">" + value + "</" + | ||
| 624 | primitiveTypeName + ">"); | ||
| 625 | return; | ||
| 626 | } | ||
| 627 | |||
| 628 | } else if (value instanceof String) { | ||
| 629 | writeln(createString((String) value)); | ||
| 630 | return; | ||
| 631 | } | ||
| 632 | |||
| 633 | if (d.name != null) { | ||
| 634 | outputXML(isArgument ? "object" : "void", " idref=" + quote(d.name), value); | ||
| 635 | } | ||
| 636 | else if (d.exp != null) { | ||
| 637 | outputStatement(d.exp, outer, isArgument); | ||
| 638 | } | ||
| 639 | } | ||
| 640 | |||
| 641 | private static String quoteCharCode(int code) { | ||
| 642 | switch(code) { | ||
| 643 | case '&': return "&"; | ||
| 644 | case '<': return "<"; | ||
| 645 | case '>': return ">"; | ||
| 646 | case '"': return """; | ||
| 647 | case '\'': return "'"; | ||
| 648 | case '\r': return " "; | ||
| 649 | default: return null; | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | private static String createString(int code) { | ||
| 654 | return "<char code=\"#" + Integer.toString(code, 16) + "\"/>"; | ||
| 655 | } | ||
| 656 | |||
| 657 | private String createString(String string) { | ||
| 658 | StringBuilder sb = new StringBuilder(); | ||
| 659 | sb.append("<string>"); | ||
| 660 | int index = 0; | ||
| 661 | while (index < string.length()) { | ||
| 662 | int point = string.codePointAt(index); | ||
| 663 | int count = Character.charCount(point); | ||
| 664 | |||
| 665 | if (isValidCharCode(point) && this.encoder.canEncode(string.substring(index, index + count))) { | ||
| 666 | String value = quoteCharCode(point); | ||
| 667 | if (value != null) { | ||
| 668 | sb.append(value); | ||
| 669 | } else { | ||
| 670 | sb.appendCodePoint(point); | ||
| 671 | } | ||
| 672 | index += count; | ||
| 673 | } else { | ||
| 674 | sb.append(createString(string.charAt(index))); | ||
| 675 | index++; | ||
| 676 | } | ||
| 677 | } | ||
| 678 | sb.append("</string>"); | ||
| 679 | return sb.toString(); | ||
| 680 | } | ||
| 681 | |||
| 682 | private void outputStatement(Statement exp, Object outer, boolean isArgument) { | ||
| 683 | Object target = exp.getTarget(); | ||
| 684 | String methodName = exp.getMethodName(); | ||
| 685 | |||
| 686 | if (target == null || methodName == null) { | ||
| 687 | throw new NullPointerException((target == null ? "target" : | ||
| 688 | "methodName") + " should not be null"); | ||
| 689 | } | ||
| 690 | |||
| 691 | Object[] args = exp.getArguments(); | ||
| 692 | boolean expression = exp.getClass() == Expression.class; | ||
| 693 | Object value = (expression) ? getValue((Expression)exp) : null; | ||
| 694 | |||
| 695 | String tag = (expression && isArgument) ? "object" : "void"; | ||
| 696 | String attributes = ""; | ||
| 697 | ValueData d = getValueData(value); | ||
| 698 | |||
| 699 | // Special cases for targets. | ||
| 700 | if (target == outer) { | ||
| 701 | } | ||
| 702 | else if (target == Array.class && methodName.equals("newInstance")) { | ||
| 703 | tag = "array"; | ||
| 704 | attributes = attributes + " class=" + quote(((Class)args[0]).getName()); | ||
| 705 | attributes = attributes + " length=" + quote(args[1].toString()); | ||
| 706 | args = new Object[]{}; | ||
| 707 | } | ||
| 708 | else if (target.getClass() == Class.class) { | ||
| 709 | attributes = attributes + " class=" + quote(((Class)target).getName()); | ||
| 710 | } | ||
| 711 | else { | ||
| 712 | d.refs = 2; | ||
| 713 | getValueData(target).refs++; | ||
| 714 | List<Statement> statements = statementList(target); | ||
| 715 | if (!statements.contains(exp)) { | ||
| 716 | statements.add(exp); | ||
| 717 | } | ||
| 718 | outputValue(target, outer, false); | ||
| 719 | if (expression) { | ||
| 720 | outputValue(value, outer, isArgument); | ||
| 721 | } | ||
| 722 | return; | ||
| 723 | } | ||
| 724 | if (expression && (d.refs > 1)) { | ||
| 725 | String instanceName = nameGenerator.instanceName(value); | ||
| 726 | d.name = instanceName; | ||
| 727 | attributes = attributes + " id=" + quote(instanceName); | ||
| 728 | } | ||
| 729 | |||
| 730 | // Special cases for methods. | ||
| 731 | if ((!expression && methodName.equals("set") && args.length == 2 && | ||
| 732 | args[0] instanceof Integer) || | ||
| 733 | (expression && methodName.equals("get") && args.length == 1 && | ||
| 734 | args[0] instanceof Integer)) { | ||
| 735 | attributes = attributes + " index=" + quote(args[0].toString()); | ||
| 736 | args = (args.length == 1) ? new Object[]{} : new Object[]{args[1]}; | ||
| 737 | } | ||
| 738 | else if ((!expression && methodName.startsWith("set") && args.length == 1) || | ||
| 739 | (expression && methodName.startsWith("get") && args.length == 0)) { | ||
| 740 | if (3 < methodName.length()) { | ||
| 741 | attributes = attributes + " property=" + | ||
| 742 | quote(Introspector.decapitalize(methodName.substring(3))); | ||
| 743 | } | ||
| 744 | } | ||
| 745 | else if (!methodName.equals("new") && !methodName.equals("newInstance")) { | ||
| 746 | attributes = attributes + " method=" + quote(methodName); | ||
| 747 | } | ||
| 748 | outputXML(tag, attributes, value, args); | ||
| 749 | } | ||
| 750 | |||
| 751 | private void outputXML(String tag, String attributes, Object value, Object... args) { | ||
| 752 | List<Statement> statements = statementList(value); | ||
| 753 | // Use XML's short form when there is no body. | ||
| 754 | if (args.length == 0 && statements.size() == 0) { | ||
| 755 | writeln("<" + tag + attributes + "/>"); | ||
| 756 | return; | ||
| 757 | } | ||
| 758 | |||
| 759 | writeln("<" + tag + attributes + ">"); | ||
| 760 | indentation++; | ||
| 761 | |||
| 762 | for(int i = 0; i < args.length; i++) { | ||
| 763 | outputValue(args[i], null, true); | ||
| 764 | } | ||
| 765 | |||
| 766 | while (!statements.isEmpty()) { | ||
| 767 | Statement s = statements.remove(0); | ||
| 768 | outputStatement(s, value, false); | ||
| 769 | } | ||
| 770 | |||
| 771 | indentation--; | ||
| 772 | writeln("</" + tag + ">"); | ||
| 773 | } | ||
| 774 | } | ||
Binary file not shown.
| @@ -0,0 +1,132 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.DesignMode; | ||
| 29 | import java.beans.Visibility; | ||
| 30 | |||
| 31 | import java.io.InputStream; | ||
| 32 | import java.io.IOException; | ||
| 33 | |||
| 34 | import java.net.URL; | ||
| 35 | |||
| 36 | import java.util.Collection; | ||
| 37 | import java.util.Locale; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * <p> | ||
| 41 | * The BeanContext acts a logical hierarchical container for JavaBeans. | ||
| 42 | * </p> | ||
| 43 | * | ||
| 44 | * @author Laurence P. G. Cable | ||
| 45 | * @since 1.2 | ||
| 46 | * | ||
| 47 | * @see java.beans.Beans | ||
| 48 | * @see java.beans.beancontext.BeanContextChild | ||
| 49 | * @see java.beans.beancontext.BeanContextMembershipListener | ||
| 50 | * @see java.beans.PropertyChangeEvent | ||
| 51 | * @see java.beans.DesignMode | ||
| 52 | * @see java.beans.Visibility | ||
| 53 | * @see java.util.Collection | ||
| 54 | */ | ||
| 55 | |||
| 56 | public interface BeanContext extends BeanContextChild, Collection, DesignMode, Visibility { | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Instantiate the javaBean named as a | ||
| 60 | * child of this <code>BeanContext</code>. | ||
| 61 | * The implementation of the JavaBean is | ||
| 62 | * derived from the value of the beanName parameter, | ||
| 63 | * and is defined by the | ||
| 64 | * <code>java.beans.Beans.instantiate()</code> method. | ||
| 65 | * | ||
| 66 | * @param beanName The name of the JavaBean to instantiate | ||
| 67 | * as a child of this <code>BeanContext</code> | ||
| 68 | * @throws <code>IOException</code> | ||
| 69 | * @throws <code>ClassNotFoundException</code> if the class identified | ||
| 70 | * by the beanName parameter is not found | ||
| 71 | */ | ||
| 72 | Object instantiateChild(String beanName) throws IOException, ClassNotFoundException; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Analagous to <code>java.lang.ClassLoader.getResourceAsStream()</code>, | ||
| 76 | * this method allows a <code>BeanContext</code> implementation | ||
| 77 | * to interpose behavior between the child <code>Component</code> | ||
| 78 | * and underlying <code>ClassLoader</code>. | ||
| 79 | * | ||
| 80 | * @param name the resource name | ||
| 81 | * @param bcc the specified child | ||
| 82 | * @return an <code>InputStream</code> for reading the resource, | ||
| 83 | * or <code>null</code> if the resource could not | ||
| 84 | * be found. | ||
| 85 | * @throws <code>IllegalArgumentException</code> if | ||
| 86 | * the resource is not valid | ||
| 87 | */ | ||
| 88 | InputStream getResourceAsStream(String name, BeanContextChild bcc) throws IllegalArgumentException; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Analagous to <code>java.lang.ClassLoader.getResource()</code>, this | ||
| 92 | * method allows a <code>BeanContext</code> implementation to interpose | ||
| 93 | * behavior between the child <code>Component</code> | ||
| 94 | * and underlying <code>ClassLoader</code>. | ||
| 95 | * | ||
| 96 | * @param name the resource name | ||
| 97 | * @param bcc the specified child | ||
| 98 | * @return a <code>URL</code> for the named | ||
| 99 | * resource for the specified child | ||
| 100 | * @throws <code>IllegalArgumentException</code> | ||
| 101 | * if the resource is not valid | ||
| 102 | */ | ||
| 103 | URL getResource(String name, BeanContextChild bcc) throws IllegalArgumentException; | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Adds the specified <code>BeanContextMembershipListener</code> | ||
| 107 | * to receive <code>BeanContextMembershipEvents</code> from | ||
| 108 | * this <code>BeanContext</code> whenever it adds | ||
| 109 | * or removes a child <code>Component</code>(s). | ||
| 110 | * | ||
| 111 | * @param bcml the <code>BeanContextMembershipListener</code> to be added | ||
| 112 | */ | ||
| 113 | void addBeanContextMembershipListener(BeanContextMembershipListener bcml); | ||
| 114 | |||
| 115 | /** | ||
| 116 | * Removes the specified <code>BeanContextMembershipListener</code> | ||
| 117 | * so that it no longer receives <code>BeanContextMembershipEvent</code>s | ||
| 118 | * when the child <code>Component</code>(s) are added or removed. | ||
| 119 | * | ||
| 120 | * @param bcml the <code>BeanContextMembershipListener</code> | ||
| 121 | * to be removed | ||
| 122 | */ | ||
| 123 | void removeBeanContextMembershipListener(BeanContextMembershipListener bcml); | ||
| 124 | |||
| 125 | /** | ||
| 126 | * This global lock is used by both <code>BeanContext</code> | ||
| 127 | * and <code>BeanContextServices</code> implementors | ||
| 128 | * to serialize changes in a <code>BeanContext</code> | ||
| 129 | * hierarchy and any service requests etc. | ||
| 130 | */ | ||
| 131 | public static final Object globalHierarchyLock = new Object(); | ||
| 132 | } | ||
Binary file not shown.
| @@ -0,0 +1,137 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.PropertyChangeListener; | ||
| 29 | import java.beans.VetoableChangeListener; | ||
| 30 | import java.beans.PropertyVetoException; | ||
| 31 | |||
| 32 | import java.beans.beancontext.BeanContext; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * <p> | ||
| 36 | * JavaBeans wishing to be nested within, and obtain a reference to their | ||
| 37 | * execution environment, or context, as defined by the BeanContext | ||
| 38 | * sub-interface shall implement this interface. | ||
| 39 | * </p> | ||
| 40 | * <p> | ||
| 41 | * Conformant BeanContexts shall as a side effect of adding a BeanContextChild | ||
| 42 | * object shall pass a reference to itself via the setBeanContext() method of | ||
| 43 | * this interface. | ||
| 44 | * </p> | ||
| 45 | * <p> | ||
| 46 | * Note that a BeanContextChild may refuse a change in state by throwing | ||
| 47 | * PropertyVetoedException in response. | ||
| 48 | * </p> | ||
| 49 | * <p> | ||
| 50 | * In order for persistence mechanisms to function properly on BeanContextChild | ||
| 51 | * instances across a broad variety of scenarios, implementing classes of this | ||
| 52 | * interface are required to define as transient, any or all fields, or | ||
| 53 | * instance variables, that may contain, or represent, references to the | ||
| 54 | * nesting BeanContext instance or other resources obtained | ||
| 55 | * from the BeanContext via any unspecified mechanisms. | ||
| 56 | * </p> | ||
| 57 | * | ||
| 58 | * @author Laurence P. G. Cable | ||
| 59 | * @since 1.2 | ||
| 60 | * | ||
| 61 | * @see java.beans.beancontext.BeanContext | ||
| 62 | * @see java.beans.PropertyChangeEvent | ||
| 63 | * @see java.beans.PropertyChangeListener | ||
| 64 | * @see java.beans.PropertyVetoException | ||
| 65 | * @see java.beans.VetoableChangeListener | ||
| 66 | */ | ||
| 67 | |||
| 68 | public interface BeanContextChild { | ||
| 69 | |||
| 70 | /** | ||
| 71 | * <p> | ||
| 72 | * Objects that implement this interface, | ||
| 73 | * shall fire a java.beans.PropertyChangeEvent, with parameters: | ||
| 74 | * | ||
| 75 | * propertyName "beanContext", oldValue (the previous nesting | ||
| 76 | * <code>BeanContext</code> instance, or <code>null</code>), | ||
| 77 | * newValue (the current nesting | ||
| 78 | * <code>BeanContext</code> instance, or <code>null</code>). | ||
| 79 | * <p> | ||
| 80 | * A change in the value of the nesting BeanContext property of this | ||
| 81 | * BeanContextChild may be vetoed by throwing the appropriate exception. | ||
| 82 | * </p> | ||
| 83 | * @param bc The <code>BeanContext</code> with which | ||
| 84 | * to associate this <code>BeanContextChild</code>. | ||
| 85 | * @throws <code>PropertyVetoException</code> if the | ||
| 86 | * addition of the specified <code>BeanContext</code> is refused. | ||
| 87 | */ | ||
| 88 | void setBeanContext(BeanContext bc) throws PropertyVetoException; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Gets the <code>BeanContext</code> associated | ||
| 92 | * with this <code>BeanContextChild</code>. | ||
| 93 | * @return the <code>BeanContext</code> associated | ||
| 94 | * with this <code>BeanContextChild</code>. | ||
| 95 | */ | ||
| 96 | BeanContext getBeanContext(); | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Adds a <code>PropertyChangeListener</code> | ||
| 100 | * to this <code>BeanContextChild</code> | ||
| 101 | * in order to receive a <code>PropertyChangeEvent</code> | ||
| 102 | * whenever the specified property has changed. | ||
| 103 | * @param name the name of the property to listen on | ||
| 104 | * @param pcl the <code>PropertyChangeListener</code> to add | ||
| 105 | */ | ||
| 106 | void addPropertyChangeListener(String name, PropertyChangeListener pcl); | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Removes a <code>PropertyChangeListener</code> from this | ||
| 110 | * <code>BeanContextChild</code> so that it no longer | ||
| 111 | * receives <code>PropertyChangeEvents</code> when the | ||
| 112 | * specified property is changed. | ||
| 113 | * | ||
| 114 | * @param name the name of the property that was listened on | ||
| 115 | * @param pcl the <code>PropertyChangeListener</code> to remove | ||
| 116 | */ | ||
| 117 | void removePropertyChangeListener(String name, PropertyChangeListener pcl); | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Adds a <code>VetoableChangeListener</code> to | ||
| 121 | * this <code>BeanContextChild</code> | ||
| 122 | * to receive events whenever the specified property changes. | ||
| 123 | * @param name the name of the property to listen on | ||
| 124 | * @param vcl the <code>VetoableChangeListener</code> to add | ||
| 125 | */ | ||
| 126 | void addVetoableChangeListener(String name, VetoableChangeListener vcl); | ||
| 127 | |||
| 128 | /** | ||
| 129 | * Removes a <code>VetoableChangeListener</code> from this | ||
| 130 | * <code>BeanContextChild</code> so that it no longer receives | ||
| 131 | * events when the specified property changes. | ||
| 132 | * @param name the name of the property that was listened on. | ||
| 133 | * @param vcl the <code>VetoableChangeListener</code> to remove. | ||
| 134 | */ | ||
| 135 | void removeVetoableChangeListener(String name, VetoableChangeListener vcl); | ||
| 136 | |||
| 137 | } | ||
Binary file not shown.
| @@ -0,0 +1,54 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2002, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.awt.Component; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * <p> | ||
| 32 | * This interface is implemented by | ||
| 33 | * <code>BeanContextChildren</code> that have an AWT <code>Component</code> | ||
| 34 | * associated with them. | ||
| 35 | * </p> | ||
| 36 | * | ||
| 37 | * @author Laurence P. G. Cable | ||
| 38 | * @since 1.2 | ||
| 39 | * | ||
| 40 | * @see java.beans.beancontext.BeanContext | ||
| 41 | * @see java.beans.beancontext.BeanContextSupport | ||
| 42 | */ | ||
| 43 | |||
| 44 | public interface BeanContextChildComponentProxy { | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Gets the <code>java.awt.Component</code> associated with | ||
| 48 | * this <code>BeanContextChild</code>. | ||
| 49 | * @return the AWT <code>Component</code> associated with | ||
| 50 | * this <code>BeanContextChild</code> | ||
| 51 | */ | ||
| 52 | |||
| 53 | Component getComponent(); | ||
| 54 | } | ||
Binary file not shown.
| @@ -0,0 +1,373 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.PropertyChangeEvent; | ||
| 29 | import java.beans.PropertyChangeListener; | ||
| 30 | import java.beans.PropertyChangeSupport; | ||
| 31 | |||
| 32 | import java.beans.VetoableChangeListener; | ||
| 33 | import java.beans.VetoableChangeSupport; | ||
| 34 | |||
| 35 | import java.beans.PropertyVetoException; | ||
| 36 | |||
| 37 | import java.io.IOException; | ||
| 38 | import java.io.ObjectInputStream; | ||
| 39 | import java.io.ObjectOutputStream; | ||
| 40 | import java.io.Serializable; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * <p> | ||
| 44 | * This is a general support class to provide support for implementing the | ||
| 45 | * BeanContextChild protocol. | ||
| 46 | * | ||
| 47 | * This class may either be directly subclassed, or encapsulated and delegated | ||
| 48 | * to in order to implement this interface for a given component. | ||
| 49 | * </p> | ||
| 50 | * | ||
| 51 | * @author Laurence P. G. Cable | ||
| 52 | * @since 1.2 | ||
| 53 | * | ||
| 54 | * @see java.beans.beancontext.BeanContext | ||
| 55 | * @see java.beans.beancontext.BeanContextServices | ||
| 56 | * @see java.beans.beancontext.BeanContextChild | ||
| 57 | */ | ||
| 58 | |||
| 59 | public class BeanContextChildSupport implements BeanContextChild, BeanContextServicesListener, Serializable { | ||
| 60 | |||
| 61 | static final long serialVersionUID = 6328947014421475877L; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * construct a BeanContextChildSupport where this class has been | ||
| 65 | * subclassed in order to implement the JavaBean component itself. | ||
| 66 | */ | ||
| 67 | |||
| 68 | public BeanContextChildSupport() { | ||
| 69 | super(); | ||
| 70 | |||
| 71 | beanContextChildPeer = this; | ||
| 72 | |||
| 73 | pcSupport = new PropertyChangeSupport(beanContextChildPeer); | ||
| 74 | vcSupport = new VetoableChangeSupport(beanContextChildPeer); | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * construct a BeanContextChildSupport where the JavaBean component | ||
| 79 | * itself implements BeanContextChild, and encapsulates this, delegating | ||
| 80 | * that interface to this implementation | ||
| 81 | */ | ||
| 82 | |||
| 83 | public BeanContextChildSupport(BeanContextChild bcc) { | ||
| 84 | super(); | ||
| 85 | |||
| 86 | beanContextChildPeer = (bcc != null) ? bcc : this; | ||
| 87 | |||
| 88 | pcSupport = new PropertyChangeSupport(beanContextChildPeer); | ||
| 89 | vcSupport = new VetoableChangeSupport(beanContextChildPeer); | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Sets the <code>BeanContext</code> for | ||
| 94 | * this <code>BeanContextChildSupport</code>. | ||
| 95 | * @param bc the new value to be assigned to the <code>BeanContext</code> | ||
| 96 | * property | ||
| 97 | * @throws <code>PropertyVetoException</code> if the change is rejected | ||
| 98 | */ | ||
| 99 | public synchronized void setBeanContext(BeanContext bc) throws PropertyVetoException { | ||
| 100 | if (bc == beanContext) return; | ||
| 101 | |||
| 102 | BeanContext oldValue = beanContext; | ||
| 103 | BeanContext newValue = bc; | ||
| 104 | |||
| 105 | if (!rejectedSetBCOnce) { | ||
| 106 | if (rejectedSetBCOnce = !validatePendingSetBeanContext(bc)) { | ||
| 107 | throw new PropertyVetoException( | ||
| 108 | "setBeanContext() change rejected:", | ||
| 109 | new PropertyChangeEvent(beanContextChildPeer, "beanContext", oldValue, newValue) | ||
| 110 | ); | ||
| 111 | } | ||
| 112 | |||
| 113 | try { | ||
| 114 | fireVetoableChange("beanContext", | ||
| 115 | oldValue, | ||
| 116 | newValue | ||
| 117 | ); | ||
| 118 | } catch (PropertyVetoException pve) { | ||
| 119 | rejectedSetBCOnce = true; | ||
| 120 | |||
| 121 | throw pve; // re-throw | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | if (beanContext != null) releaseBeanContextResources(); | ||
| 126 | |||
| 127 | beanContext = newValue; | ||
| 128 | rejectedSetBCOnce = false; | ||
| 129 | |||
| 130 | firePropertyChange("beanContext", | ||
| 131 | oldValue, | ||
| 132 | newValue | ||
| 133 | ); | ||
| 134 | |||
| 135 | if (beanContext != null) initializeBeanContextResources(); | ||
| 136 | } | ||
| 137 | |||
| 138 | /** | ||
| 139 | * Gets the nesting <code>BeanContext</code> | ||
| 140 | * for this <code>BeanContextChildSupport</code>. | ||
| 141 | * @return the nesting <code>BeanContext</code> for | ||
| 142 | * this <code>BeanContextChildSupport</code>. | ||
| 143 | */ | ||
| 144 | public synchronized BeanContext getBeanContext() { return beanContext; } | ||
| 145 | |||
| 146 | /** | ||
| 147 | * Add a PropertyChangeListener for a specific property. | ||
| 148 | * The same listener object may be added more than once. For each | ||
| 149 | * property, the listener will be invoked the number of times it was added | ||
| 150 | * for that property. | ||
| 151 | * If <code>name</code> or <code>pcl</code> is null, no exception is thrown | ||
| 152 | * and no action is taken. | ||
| 153 | * | ||
| 154 | * @param name The name of the property to listen on | ||
| 155 | * @param pcl The <code>PropertyChangeListener</code> to be added | ||
| 156 | */ | ||
| 157 | public void addPropertyChangeListener(String name, PropertyChangeListener pcl) { | ||
| 158 | pcSupport.addPropertyChangeListener(name, pcl); | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Remove a PropertyChangeListener for a specific property. | ||
| 163 | * If <code>pcl</code> was added more than once to the same event | ||
| 164 | * source for the specified property, it will be notified one less time | ||
| 165 | * after being removed. | ||
| 166 | * If <code>name</code> is null, no exception is thrown | ||
| 167 | * and no action is taken. | ||
| 168 | * If <code>pcl</code> is null, or was never added for the specified | ||
| 169 | * property, no exception is thrown and no action is taken. | ||
| 170 | * | ||
| 171 | * @param name The name of the property that was listened on | ||
| 172 | * @param pcl The PropertyChangeListener to be removed | ||
| 173 | */ | ||
| 174 | public void removePropertyChangeListener(String name, PropertyChangeListener pcl) { | ||
| 175 | pcSupport.removePropertyChangeListener(name, pcl); | ||
| 176 | } | ||
| 177 | |||
| 178 | /** | ||
| 179 | * Add a VetoableChangeListener for a specific property. | ||
| 180 | * The same listener object may be added more than once. For each | ||
| 181 | * property, the listener will be invoked the number of times it was added | ||
| 182 | * for that property. | ||
| 183 | * If <code>name</code> or <code>vcl</code> is null, no exception is thrown | ||
| 184 | * and no action is taken. | ||
| 185 | * | ||
| 186 | * @param name The name of the property to listen on | ||
| 187 | * @param vcl The <code>VetoableChangeListener</code> to be added | ||
| 188 | */ | ||
| 189 | public void addVetoableChangeListener(String name, VetoableChangeListener vcl) { | ||
| 190 | vcSupport.addVetoableChangeListener(name, vcl); | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Removes a <code>VetoableChangeListener</code>. | ||
| 195 | * If <code>pcl</code> was added more than once to the same event | ||
| 196 | * source for the specified property, it will be notified one less time | ||
| 197 | * after being removed. | ||
| 198 | * If <code>name</code> is null, no exception is thrown | ||
| 199 | * and no action is taken. | ||
| 200 | * If <code>vcl</code> is null, or was never added for the specified | ||
| 201 | * property, no exception is thrown and no action is taken. | ||
| 202 | * | ||
| 203 | * @param name The name of the property that was listened on | ||
| 204 | * @param vcl The <code>VetoableChangeListener</code> to be removed | ||
| 205 | */ | ||
| 206 | public void removeVetoableChangeListener(String name, VetoableChangeListener vcl) { | ||
| 207 | vcSupport.removeVetoableChangeListener(name, vcl); | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * A service provided by the nesting BeanContext has been revoked. | ||
| 212 | * | ||
| 213 | * Subclasses may override this method in order to implement their own | ||
| 214 | * behaviors. | ||
| 215 | * @param bcsre The <code>BeanContextServiceRevokedEvent</code> fired as a | ||
| 216 | * result of a service being revoked | ||
| 217 | */ | ||
| 218 | public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) { } | ||
| 219 | |||
| 220 | /** | ||
| 221 | * A new service is available from the nesting BeanContext. | ||
| 222 | * | ||
| 223 | * Subclasses may override this method in order to implement their own | ||
| 224 | * behaviors | ||
| 225 | * @param bcsae The BeanContextServiceAvailableEvent fired as a | ||
| 226 | * result of a service becoming available | ||
| 227 | * | ||
| 228 | */ | ||
| 229 | public void serviceAvailable(BeanContextServiceAvailableEvent bcsae) { } | ||
| 230 | |||
| 231 | /** | ||
| 232 | * Gets the <tt>BeanContextChild</tt> associated with this | ||
| 233 | * <tt>BeanContextChildSupport</tt>. | ||
| 234 | * | ||
| 235 | * @return the <tt>BeanContextChild</tt> peer of this class | ||
| 236 | */ | ||
| 237 | public BeanContextChild getBeanContextChildPeer() { return beanContextChildPeer; } | ||
| 238 | |||
| 239 | /** | ||
| 240 | * Reports whether or not this class is a delegate of another. | ||
| 241 | * | ||
| 242 | * @return true if this class is a delegate of another | ||
| 243 | */ | ||
| 244 | public boolean isDelegated() { return !this.equals(beanContextChildPeer); } | ||
| 245 | |||
| 246 | /** | ||
| 247 | * Report a bound property update to any registered listeners. No event is | ||
| 248 | * fired if old and new are equal and non-null. | ||
| 249 | * @param name The programmatic name of the property that was changed | ||
| 250 | * @param oldValue The old value of the property | ||
| 251 | * @param newValue The new value of the property | ||
| 252 | */ | ||
| 253 | public void firePropertyChange(String name, Object oldValue, Object newValue) { | ||
| 254 | pcSupport.firePropertyChange(name, oldValue, newValue); | ||
| 255 | } | ||
| 256 | |||
| 257 | /** | ||
| 258 | * Report a vetoable property update to any registered listeners. | ||
| 259 | * If anyone vetos the change, then fire a new event | ||
| 260 | * reverting everyone to the old value and then rethrow | ||
| 261 | * the PropertyVetoException. <P> | ||
| 262 | * | ||
| 263 | * No event is fired if old and new are equal and non-null. | ||
| 264 | * <P> | ||
| 265 | * @param name The programmatic name of the property that is about to | ||
| 266 | * change | ||
| 267 | * | ||
| 268 | * @param oldValue The old value of the property | ||
| 269 | * @param newValue - The new value of the property | ||
| 270 | * | ||
| 271 | * @throws PropertyVetoException if the recipient wishes the property | ||
| 272 | * change to be rolled back. | ||
| 273 | */ | ||
| 274 | public void fireVetoableChange(String name, Object oldValue, Object newValue) throws PropertyVetoException { | ||
| 275 | vcSupport.fireVetoableChange(name, oldValue, newValue); | ||
| 276 | } | ||
| 277 | |||
| 278 | /** | ||
| 279 | * Called from setBeanContext to validate (or otherwise) the | ||
| 280 | * pending change in the nesting BeanContext property value. | ||
| 281 | * Returning false will cause setBeanContext to throw | ||
| 282 | * PropertyVetoException. | ||
| 283 | * @param newValue the new value that has been requested for | ||
| 284 | * the BeanContext property | ||
| 285 | * @return <code>true</code> if the change operation is to be vetoed | ||
| 286 | */ | ||
| 287 | public boolean validatePendingSetBeanContext(BeanContext newValue) { | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | /** | ||
| 292 | * This method may be overridden by subclasses to provide their own | ||
| 293 | * release behaviors. When invoked any resources held by this instance | ||
| 294 | * obtained from its current BeanContext property should be released | ||
| 295 | * since the object is no longer nested within that BeanContext. | ||
| 296 | */ | ||
| 297 | |||
| 298 | protected void releaseBeanContextResources() { | ||
| 299 | // do nothing | ||
| 300 | } | ||
| 301 | |||
| 302 | /** | ||
| 303 | * This method may be overridden by subclasses to provide their own | ||
| 304 | * initialization behaviors. When invoked any resources requried by the | ||
| 305 | * BeanContextChild should be obtained from the current BeanContext. | ||
| 306 | */ | ||
| 307 | |||
| 308 | protected void initializeBeanContextResources() { | ||
| 309 | // do nothing | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * Write the persistence state of the object. | ||
| 314 | */ | ||
| 315 | |||
| 316 | private void writeObject(ObjectOutputStream oos) throws IOException { | ||
| 317 | |||
| 318 | /* | ||
| 319 | * dont serialize if we are delegated and the delegator isnt also | ||
| 320 | * serializable. | ||
| 321 | */ | ||
| 322 | |||
| 323 | if (!equals(beanContextChildPeer) && !(beanContextChildPeer instanceof Serializable)) | ||
| 324 | throw new IOException("BeanContextChildSupport beanContextChildPeer not Serializable"); | ||
| 325 | |||
| 326 | else | ||
| 327 | oos.defaultWriteObject(); | ||
| 328 | |||
| 329 | } | ||
| 330 | |||
| 331 | |||
| 332 | /** | ||
| 333 | * Restore a persistent object, must wait for subsequent setBeanContext() | ||
| 334 | * to fully restore any resources obtained from the new nesting | ||
| 335 | * BeanContext | ||
| 336 | */ | ||
| 337 | |||
| 338 | private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { | ||
| 339 | ois.defaultReadObject(); | ||
| 340 | } | ||
| 341 | |||
| 342 | /* | ||
| 343 | * fields | ||
| 344 | */ | ||
| 345 | |||
| 346 | /** | ||
| 347 | * The <code>BeanContext</code> in which | ||
| 348 | * this <code>BeanContextChild</code> is nested. | ||
| 349 | */ | ||
| 350 | public BeanContextChild beanContextChildPeer; | ||
| 351 | |||
| 352 | /** | ||
| 353 | * The <tt>PropertyChangeSupport</tt> associated with this | ||
| 354 | * <tt>BeanContextChildSupport</tt>. | ||
| 355 | */ | ||
| 356 | protected PropertyChangeSupport pcSupport; | ||
| 357 | |||
| 358 | /** | ||
| 359 | * The <tt>VetoableChangeSupport</tt> associated with this | ||
| 360 | * <tt>BeanContextChildSupport</tt>. | ||
| 361 | */ | ||
| 362 | protected VetoableChangeSupport vcSupport; | ||
| 363 | |||
| 364 | protected transient BeanContext beanContext; | ||
| 365 | |||
| 366 | /** | ||
| 367 | * A flag indicating that there has been | ||
| 368 | * at least one <code>PropertyChangeVetoException</code> | ||
| 369 | * thrown for the attempted setBeanContext operation. | ||
| 370 | */ | ||
| 371 | protected transient boolean rejectedSetBCOnce; | ||
| 372 | |||
| 373 | } | ||
Binary file not shown.
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2002, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.awt.Container; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * <p> | ||
| 32 | * This interface is implemented by BeanContexts' that have an AWT Container | ||
| 33 | * associated with them. | ||
| 34 | * </p> | ||
| 35 | * | ||
| 36 | * @author Laurence P. G. Cable | ||
| 37 | * @since 1.2 | ||
| 38 | * | ||
| 39 | * @see java.beans.beancontext.BeanContext | ||
| 40 | * @see java.beans.beancontext.BeanContextSupport | ||
| 41 | */ | ||
| 42 | |||
| 43 | public interface BeanContextContainerProxy { | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Gets the <code>java.awt.Container</code> associated | ||
| 47 | * with this <code>BeanContext</code>. | ||
| 48 | * @return the <code>java.awt.Container</code> associated | ||
| 49 | * with this <code>BeanContext</code>. | ||
| 50 | */ | ||
| 51 | Container getContainer(); | ||
| 52 | } | ||
Binary file not shown.
| @@ -0,0 +1,104 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.util.EventObject; | ||
| 29 | |||
| 30 | import java.beans.beancontext.BeanContext; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * <p> | ||
| 34 | * <code>BeanContextEvent</code> is the abstract root event class | ||
| 35 | * for all events emitted | ||
| 36 | * from, and pertaining to the semantics of, a <code>BeanContext</code>. | ||
| 37 | * This class introduces a mechanism to allow the propagation of | ||
| 38 | * <code>BeanContextEvent</code> subclasses through a hierarchy of | ||
| 39 | * <code>BeanContext</code>s. The <code>setPropagatedFrom()</code> | ||
| 40 | * and <code>getPropagatedFrom()</code> methods allow a | ||
| 41 | * <code>BeanContext</code> to identify itself as the source | ||
| 42 | * of a propagated event. | ||
| 43 | * </p> | ||
| 44 | * | ||
| 45 | * @author Laurence P. G. Cable | ||
| 46 | * @since 1.2 | ||
| 47 | * @see java.beans.beancontext.BeanContext | ||
| 48 | */ | ||
| 49 | |||
| 50 | public abstract class BeanContextEvent extends EventObject { | ||
| 51 | private static final long serialVersionUID = 7267998073569045052L; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Contruct a BeanContextEvent | ||
| 55 | * | ||
| 56 | * @param bc The BeanContext source | ||
| 57 | */ | ||
| 58 | protected BeanContextEvent(BeanContext bc) { | ||
| 59 | super(bc); | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Gets the <code>BeanContext</code> associated with this event. | ||
| 64 | * @return the <code>BeanContext</code> associated with this event. | ||
| 65 | */ | ||
| 66 | public BeanContext getBeanContext() { return (BeanContext)getSource(); } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Sets the <code>BeanContext</code> from which this event was propagated. | ||
| 70 | * @param bc the <code>BeanContext</code> from which this event | ||
| 71 | * was propagated | ||
| 72 | */ | ||
| 73 | public synchronized void setPropagatedFrom(BeanContext bc) { | ||
| 74 | propagatedFrom = bc; | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Gets the <code>BeanContext</code> from which this event was propagated. | ||
| 79 | * @return the <code>BeanContext</code> from which this | ||
| 80 | * event was propagated | ||
| 81 | */ | ||
| 82 | public synchronized BeanContext getPropagatedFrom() { | ||
| 83 | return propagatedFrom; | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Reports whether or not this event is | ||
| 88 | * propagated from some other <code>BeanContext</code>. | ||
| 89 | * @return <code>true</code> if propagated, <code>false</code> | ||
| 90 | * if not | ||
| 91 | */ | ||
| 92 | public synchronized boolean isPropagated() { | ||
| 93 | return propagatedFrom != null; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* | ||
| 97 | * fields | ||
| 98 | */ | ||
| 99 | |||
| 100 | /** | ||
| 101 | * The <code>BeanContext</code> from which this event was propagated | ||
| 102 | */ | ||
| 103 | protected BeanContext propagatedFrom; | ||
| 104 | } | ||
Binary file not shown.
| @@ -0,0 +1,131 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.util.EventObject; | ||
| 29 | |||
| 30 | import java.beans.beancontext.BeanContext; | ||
| 31 | import java.beans.beancontext.BeanContextEvent; | ||
| 32 | |||
| 33 | import java.util.Arrays; | ||
| 34 | import java.util.Collection; | ||
| 35 | import java.util.Iterator; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * A <code>BeanContextMembershipEvent</code> encapsulates | ||
| 39 | * the list of children added to, or removed from, | ||
| 40 | * the membership of a particular <code>BeanContext</code>. | ||
| 41 | * An instance of this event is fired whenever a successful | ||
| 42 | * add(), remove(), retainAll(), removeAll(), or clear() is | ||
| 43 | * invoked on a given <code>BeanContext</code> instance. | ||
| 44 | * Objects interested in receiving events of this type must | ||
| 45 | * implement the <code>BeanContextMembershipListener</code> | ||
| 46 | * interface, and must register their intent via the | ||
| 47 | * <code>BeanContext</code>'s | ||
| 48 | * <code>addBeanContextMembershipListener(BeanContextMembershipListener bcml) | ||
| 49 | * </code> method. | ||
| 50 | * | ||
| 51 | * @author Laurence P. G. Cable | ||
| 52 | * @since 1.2 | ||
| 53 | * @see java.beans.beancontext.BeanContext | ||
| 54 | * @see java.beans.beancontext.BeanContextEvent | ||
| 55 | * @see java.beans.beancontext.BeanContextMembershipListener | ||
| 56 | */ | ||
| 57 | public class BeanContextMembershipEvent extends BeanContextEvent { | ||
| 58 | private static final long serialVersionUID = 3499346510334590959L; | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Contruct a BeanContextMembershipEvent | ||
| 62 | * | ||
| 63 | * @param bc The BeanContext source | ||
| 64 | * @param changes The Children affected | ||
| 65 | * @throws NullPointerException if <CODE>changes</CODE> is <CODE>null</CODE> | ||
| 66 | */ | ||
| 67 | |||
| 68 | public BeanContextMembershipEvent(BeanContext bc, Collection changes) { | ||
| 69 | super(bc); | ||
| 70 | |||
| 71 | if (changes == null) throw new NullPointerException( | ||
| 72 | "BeanContextMembershipEvent constructor: changes is null."); | ||
| 73 | |||
| 74 | children = changes; | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Contruct a BeanContextMembershipEvent | ||
| 79 | * | ||
| 80 | * @param bc The BeanContext source | ||
| 81 | * @param changes The Children effected | ||
| 82 | * @exception NullPointerException if changes associated with this | ||
| 83 | * event are null. | ||
| 84 | */ | ||
| 85 | |||
| 86 | public BeanContextMembershipEvent(BeanContext bc, Object[] changes) { | ||
| 87 | super(bc); | ||
| 88 | |||
| 89 | if (changes == null) throw new NullPointerException( | ||
| 90 | "BeanContextMembershipEvent: changes is null."); | ||
| 91 | |||
| 92 | children = Arrays.asList(changes); | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Gets the number of children affected by the notification. | ||
| 97 | * @return the number of children affected by the notification | ||
| 98 | */ | ||
| 99 | public int size() { return children.size(); } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Is the child specified affected by the event? | ||
| 103 | * @return <code>true</code> if affected, <code>false</code> | ||
| 104 | * if not | ||
| 105 | */ | ||
| 106 | public boolean contains(Object child) { | ||
| 107 | return children.contains(child); | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Gets the array of children affected by this event. | ||
| 112 | * @return the array of children affected | ||
| 113 | */ | ||
| 114 | public Object[] toArray() { return children.toArray(); } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Gets the array of children affected by this event. | ||
| 118 | * @return the array of children effected | ||
| 119 | */ | ||
| 120 | public Iterator iterator() { return children.iterator(); } | ||
| 121 | |||
| 122 | /* | ||
| 123 | * fields | ||
| 124 | */ | ||
| 125 | |||
| 126 | /** | ||
| 127 | * The list of children affected by this | ||
| 128 | * event notification. | ||
| 129 | */ | ||
| 130 | protected Collection children; | ||
| 131 | } | ||
Binary file not shown.
| @@ -0,0 +1,61 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.beancontext.BeanContextMembershipEvent; | ||
| 29 | |||
| 30 | import java.util.EventListener; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * <p> | ||
| 34 | * Compliant BeanContexts fire events on this interface when the state of | ||
| 35 | * the membership of the BeanContext changes. | ||
| 36 | * </p> | ||
| 37 | * | ||
| 38 | * @author Laurence P. G. Cable | ||
| 39 | * @since 1.2 | ||
| 40 | * @see java.beans.beancontext.BeanContext | ||
| 41 | */ | ||
| 42 | |||
| 43 | public interface BeanContextMembershipListener extends EventListener { | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Called when a child or list of children is added to a | ||
| 47 | * <code>BeanContext</code> that this listener is registered with. | ||
| 48 | * @param bcme The <code>BeanContextMembershipEvent</code> | ||
| 49 | * describing the change that occurred. | ||
| 50 | */ | ||
| 51 | void childrenAdded(BeanContextMembershipEvent bcme); | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Called when a child or list of children is removed | ||
| 55 | * from a <code>BeanContext</code> that this listener | ||
| 56 | * is registered with. | ||
| 57 | * @param bcme The <code>BeanContextMembershipEvent</code> | ||
| 58 | * describing the change that occurred. | ||
| 59 | */ | ||
| 60 | void childrenRemoved(BeanContextMembershipEvent bcme); | ||
| 61 | } | ||
Binary file not shown.
| @@ -0,0 +1,85 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2002, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * <p> | ||
| 30 | * This interface is implemented by a JavaBean that does | ||
| 31 | * not directly have a BeanContext(Child) associated with | ||
| 32 | * it (via implementing that interface or a subinterface thereof), | ||
| 33 | * but has a public BeanContext(Child) delegated from it. | ||
| 34 | * For example, a subclass of java.awt.Container may have a BeanContext | ||
| 35 | * associated with it that all Component children of that Container shall | ||
| 36 | * be contained within. | ||
| 37 | * </p> | ||
| 38 | * <p> | ||
| 39 | * An Object may not implement this interface and the | ||
| 40 | * BeanContextChild interface | ||
| 41 | * (or any subinterfaces thereof) they are mutually exclusive. | ||
| 42 | * </p> | ||
| 43 | * <p> | ||
| 44 | * Callers of this interface shall examine the return type in order to | ||
| 45 | * obtain a particular subinterface of BeanContextChild as follows: | ||
| 46 | * <code> | ||
| 47 | * BeanContextChild bcc = o.getBeanContextProxy(); | ||
| 48 | * | ||
| 49 | * if (bcc instanceof BeanContext) { | ||
| 50 | * // ... | ||
| 51 | * } | ||
| 52 | * </code> | ||
| 53 | * or | ||
| 54 | * <code> | ||
| 55 | * BeanContextChild bcc = o.getBeanContextProxy(); | ||
| 56 | * BeanContext bc = null; | ||
| 57 | * | ||
| 58 | * try { | ||
| 59 | * bc = (BeanContext)bcc; | ||
| 60 | * } catch (ClassCastException cce) { | ||
| 61 | * // cast failed, bcc is not an instanceof BeanContext | ||
| 62 | * } | ||
| 63 | * </code> | ||
| 64 | * </p> | ||
| 65 | * <p> | ||
| 66 | * The return value is a constant for the lifetime of the implementing | ||
| 67 | * instance | ||
| 68 | * </p> | ||
| 69 | * @author Laurence P. G. Cable | ||
| 70 | * @since 1.2 | ||
| 71 | * | ||
| 72 | * @see java.beans.beancontext.BeanContextChild | ||
| 73 | * @see java.beans.beancontext.BeanContextChildSupport | ||
| 74 | */ | ||
| 75 | |||
| 76 | public interface BeanContextProxy { | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Gets the <code>BeanContextChild</code> (or subinterface) | ||
| 80 | * associated with this object. | ||
| 81 | * @return the <code>BeanContextChild</code> (or subinterface) | ||
| 82 | * associated with this object | ||
| 83 | */ | ||
| 84 | BeanContextChild getBeanContextProxy(); | ||
| 85 | } | ||
Binary file not shown.
| @@ -0,0 +1,86 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.beancontext.BeanContextChild; | ||
| 29 | import java.beans.beancontext.BeanContextEvent; | ||
| 30 | |||
| 31 | import java.beans.beancontext.BeanContextServices; | ||
| 32 | |||
| 33 | import java.util.Iterator; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * <p> | ||
| 37 | * This event type is used by the BeanContextServicesListener in order to | ||
| 38 | * identify the service being registered. | ||
| 39 | * </p> | ||
| 40 | */ | ||
| 41 | |||
| 42 | public class BeanContextServiceAvailableEvent extends BeanContextEvent { | ||
| 43 | private static final long serialVersionUID = -5333985775656400778L; | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Construct a <code>BeanContextAvailableServiceEvent</code>. | ||
| 47 | * @param bcs The context in which the service has become available | ||
| 48 | * @param sc A <code>Class</code> reference to the newly available service | ||
| 49 | */ | ||
| 50 | public BeanContextServiceAvailableEvent(BeanContextServices bcs, Class sc) { | ||
| 51 | super((BeanContext)bcs); | ||
| 52 | |||
| 53 | serviceClass = sc; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Gets the source as a reference of type <code>BeanContextServices</code>. | ||
| 58 | * @return The context in which the service has become available | ||
| 59 | */ | ||
| 60 | public BeanContextServices getSourceAsBeanContextServices() { | ||
| 61 | return (BeanContextServices)getBeanContext(); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Gets the service class that is the subject of this notification. | ||
| 66 | * @return A <code>Class</code> reference to the newly available service | ||
| 67 | */ | ||
| 68 | public Class getServiceClass() { return serviceClass; } | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Gets the list of service dependent selectors. | ||
| 72 | * @return the current selectors available from the service | ||
| 73 | */ | ||
| 74 | public Iterator getCurrentServiceSelectors() { | ||
| 75 | return ((BeanContextServices)getSource()).getCurrentServiceSelectors(serviceClass); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | * fields | ||
| 80 | */ | ||
| 81 | |||
| 82 | /** | ||
| 83 | * A <code>Class</code> reference to the newly available service | ||
| 84 | */ | ||
| 85 | protected Class serviceClass; | ||
| 86 | } | ||
Binary file not shown.
| @@ -0,0 +1,104 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.util.Iterator; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * <p> | ||
| 32 | * One of the primary functions of a BeanContext is to act a as rendezvous | ||
| 33 | * between JavaBeans, and BeanContextServiceProviders. | ||
| 34 | * </p> | ||
| 35 | * <p> | ||
| 36 | * A JavaBean nested within a BeanContext, may ask that BeanContext to | ||
| 37 | * provide an instance of a "service", based upon a reference to a Java | ||
| 38 | * Class object that represents that service. | ||
| 39 | * </p> | ||
| 40 | * <p> | ||
| 41 | * If such a service has been registered with the context, or one of its | ||
| 42 | * nesting context's, in the case where a context delegate to its context | ||
| 43 | * to satisfy a service request, then the BeanContextServiceProvider associated with | ||
| 44 | * the service is asked to provide an instance of that service. | ||
| 45 | * </p> | ||
| 46 | * <p> | ||
| 47 | * The ServcieProvider may always return the same instance, or it may | ||
| 48 | * construct a new instance for each request. | ||
| 49 | * </p> | ||
| 50 | */ | ||
| 51 | |||
| 52 | public interface BeanContextServiceProvider { | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Invoked by <code>BeanContextServices</code>, this method | ||
| 56 | * requests an instance of a | ||
| 57 | * service from this <code>BeanContextServiceProvider</code>. | ||
| 58 | * | ||
| 59 | * @param bcs The <code>BeanContextServices</code> associated with this | ||
| 60 | * particular request. This parameter enables the | ||
| 61 | * <code>BeanContextServiceProvider</code> to distinguish service | ||
| 62 | * requests from multiple sources. | ||
| 63 | * | ||
| 64 | * @param requestor The object requesting the service | ||
| 65 | * | ||
| 66 | * @param serviceClass The service requested | ||
| 67 | * | ||
| 68 | * @param serviceSelector the service dependent parameter | ||
| 69 | * for a particular service, or <code>null</code> if not applicable. | ||
| 70 | * | ||
| 71 | * @return a reference to the requested service | ||
| 72 | */ | ||
| 73 | Object getService(BeanContextServices bcs, Object requestor, Class serviceClass, Object serviceSelector); | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Invoked by <code>BeanContextServices</code>, | ||
| 77 | * this method releases a nested <code>BeanContextChild</code>'s | ||
| 78 | * (or any arbitrary object associated with a | ||
| 79 | * <code>BeanContextChild</code>) reference to the specified service. | ||
| 80 | * | ||
| 81 | * @param bcs the <code>BeanContextServices</code> associated with this | ||
| 82 | * particular release request | ||
| 83 | * | ||
| 84 | * @param requestor the object requesting the service to be released | ||
| 85 | * | ||
| 86 | * @param service the service that is to be released | ||
| 87 | */ | ||
| 88 | public void releaseService(BeanContextServices bcs, Object requestor, Object service); | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Invoked by <code>BeanContextServices</code>, this method | ||
| 92 | * gets the current service selectors for the specified service. | ||
| 93 | * A service selector is a service specific parameter, | ||
| 94 | * typical examples of which could include: a | ||
| 95 | * parameter to a constructor for the service implementation class, | ||
| 96 | * a value for a particular service's property, or a key into a | ||
| 97 | * map of existing implementations. | ||
| 98 | * | ||
| 99 | * @param bcs the <code>BeanContextServices</code> for this request | ||
| 100 | * @param serviceClass the specified service | ||
| 101 | * @return the current service selectors for the specified serviceClass | ||
| 102 | */ | ||
| 103 | Iterator getCurrentServiceSelectors(BeanContextServices bcs, Class serviceClass); | ||
| 104 | } | ||
Binary file not shown.
| @@ -0,0 +1,47 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.BeanInfo; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A BeanContextServiceProvider implementor who wishes to provide explicit | ||
| 32 | * information about the services their bean may provide shall implement a | ||
| 33 | * BeanInfo class that implements this BeanInfo subinterface and provides | ||
| 34 | * explicit information about the methods, properties, events, etc, of their | ||
| 35 | * services. | ||
| 36 | */ | ||
| 37 | |||
| 38 | public interface BeanContextServiceProviderBeanInfo extends BeanInfo { | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Gets a <code>BeanInfo</code> array, one for each | ||
| 42 | * service class or interface statically available | ||
| 43 | * from this ServiceProvider. | ||
| 44 | * @return the <code>BeanInfo</code> array | ||
| 45 | */ | ||
| 46 | BeanInfo[] getServicesBeanInfo(); | ||
| 47 | } | ||
Binary file not shown.
| @@ -0,0 +1,99 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.beancontext.BeanContextEvent; | ||
| 29 | |||
| 30 | import java.beans.beancontext.BeanContextServices; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * <p> | ||
| 34 | * This event type is used by the | ||
| 35 | * <code>BeanContextServiceRevokedListener</code> in order to | ||
| 36 | * identify the service being revoked. | ||
| 37 | * </p> | ||
| 38 | */ | ||
| 39 | public class BeanContextServiceRevokedEvent extends BeanContextEvent { | ||
| 40 | private static final long serialVersionUID = -1295543154724961754L; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Construct a <code>BeanContextServiceEvent</code>. | ||
| 44 | * @param bcs the <code>BeanContextServices</code> | ||
| 45 | * from which this service is being revoked | ||
| 46 | * @param sc the service that is being revoked | ||
| 47 | * @param invalidate <code>true</code> for immediate revocation | ||
| 48 | */ | ||
| 49 | public BeanContextServiceRevokedEvent(BeanContextServices bcs, Class sc, boolean invalidate) { | ||
| 50 | super((BeanContext)bcs); | ||
| 51 | |||
| 52 | serviceClass = sc; | ||
| 53 | invalidateRefs = invalidate; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Gets the source as a reference of type <code>BeanContextServices</code> | ||
| 58 | * @return the <code>BeanContextServices</code> from which | ||
| 59 | * this service is being revoked | ||
| 60 | */ | ||
| 61 | public BeanContextServices getSourceAsBeanContextServices() { | ||
| 62 | return (BeanContextServices)getBeanContext(); | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Gets the service class that is the subject of this notification | ||
| 67 | * @return A <code>Class</code> reference to the | ||
| 68 | * service that is being revoked | ||
| 69 | */ | ||
| 70 | public Class getServiceClass() { return serviceClass; } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Checks this event to determine whether or not | ||
| 74 | * the service being revoked is of a particular class. | ||
| 75 | * @param service the service of interest (should be non-null) | ||
| 76 | * @return <code>true</code> if the service being revoked is of the | ||
| 77 | * same class as the specified service | ||
| 78 | */ | ||
| 79 | public boolean isServiceClass(Class service) { | ||
| 80 | return serviceClass.equals(service); | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Reports if the current service is being forcibly revoked, | ||
| 85 | * in which case the references are now invalidated and unusable. | ||
| 86 | * @return <code>true</code> if current service is being forcibly revoked | ||
| 87 | */ | ||
| 88 | public boolean isCurrentServiceInvalidNow() { return invalidateRefs; } | ||
| 89 | |||
| 90 | /** | ||
| 91 | * fields | ||
| 92 | */ | ||
| 93 | |||
| 94 | /** | ||
| 95 | * A <code>Class</code> reference to the service that is being revoked. | ||
| 96 | */ | ||
| 97 | protected Class serviceClass; | ||
| 98 | private boolean invalidateRefs; | ||
| 99 | } | ||
Binary file not shown.
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | package java.beans.beancontext; | ||
| 26 | |||
| 27 | import java.beans.beancontext.BeanContextServiceRevokedEvent; | ||
| 28 | |||
| 29 | import java.util.EventListener; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * The listener interface for receiving | ||
| 33 | * <code>BeanContextServiceRevokedEvent</code> objects. A class that is | ||
| 34 | * interested in processing a <code>BeanContextServiceRevokedEvent</code> | ||
| 35 | * implements this interface. | ||
| 36 | */ | ||
| 37 | public interface BeanContextServiceRevokedListener extends EventListener { | ||
| 38 | |||
| 39 | /** | ||
| 40 | * The service named has been revoked. getService requests for | ||
| 41 | * this service will no longer be satisifed. | ||
| 42 | * @param bcsre the <code>BeanContextServiceRevokedEvent</code> received | ||
| 43 | * by this listener. | ||
| 44 | */ | ||
| 45 | void serviceRevoked(BeanContextServiceRevokedEvent bcsre); | ||
| 46 | } | ||
Binary file not shown.
| @@ -0,0 +1,159 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.util.Iterator; | ||
| 29 | |||
| 30 | import java.util.TooManyListenersException; | ||
| 31 | |||
| 32 | import java.beans.beancontext.BeanContext; | ||
| 33 | |||
| 34 | import java.beans.beancontext.BeanContextServiceProvider; | ||
| 35 | |||
| 36 | import java.beans.beancontext.BeanContextServicesListener; | ||
| 37 | |||
| 38 | |||
| 39 | /** | ||
| 40 | * <p> | ||
| 41 | * The BeanContextServices interface provides a mechanism for a BeanContext | ||
| 42 | * to expose generic "services" to the BeanContextChild objects within. | ||
| 43 | * </p> | ||
| 44 | */ | ||
| 45 | public interface BeanContextServices extends BeanContext, BeanContextServicesListener { | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Adds a service to this BeanContext. | ||
| 49 | * <code>BeanContextServiceProvider</code>s call this method | ||
| 50 | * to register a particular service with this context. | ||
| 51 | * If the service has not previously been added, the | ||
| 52 | * <code>BeanContextServices</code> associates | ||
| 53 | * the service with the <code>BeanContextServiceProvider</code> and | ||
| 54 | * fires a <code>BeanContextServiceAvailableEvent</code> to all | ||
| 55 | * currently registered <code>BeanContextServicesListeners</code>. | ||
| 56 | * The method then returns <code>true</code>, indicating that | ||
| 57 | * the addition of the service was successful. | ||
| 58 | * If the given service has already been added, this method | ||
| 59 | * simply returns <code>false</code>. | ||
| 60 | * @param serviceClass the service to add | ||
| 61 | * @param serviceProvider the <code>BeanContextServiceProvider</code> | ||
| 62 | * associated with the service | ||
| 63 | */ | ||
| 64 | boolean addService(Class serviceClass, BeanContextServiceProvider serviceProvider); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * BeanContextServiceProviders wishing to remove | ||
| 68 | * a currently registered service from this context | ||
| 69 | * may do so via invocation of this method. Upon revocation of | ||
| 70 | * the service, the <code>BeanContextServices</code> fires a | ||
| 71 | * <code>BeanContextServiceRevokedEvent</code> to its | ||
| 72 | * list of currently registered | ||
| 73 | * <code>BeanContextServiceRevokedListeners</code> and | ||
| 74 | * <code>BeanContextServicesListeners</code>. | ||
| 75 | * @param serviceClass the service to revoke from this BeanContextServices | ||
| 76 | * @param serviceProvider the BeanContextServiceProvider associated with | ||
| 77 | * this particular service that is being revoked | ||
| 78 | * @param revokeCurrentServicesNow a value of <code>true</code> | ||
| 79 | * indicates an exceptional circumstance where the | ||
| 80 | * <code>BeanContextServiceProvider</code> or | ||
| 81 | * <code>BeanContextServices</code> wishes to immediately | ||
| 82 | * terminate service to all currently outstanding references | ||
| 83 | * to the specified service. | ||
| 84 | */ | ||
| 85 | void revokeService(Class serviceClass, BeanContextServiceProvider serviceProvider, boolean revokeCurrentServicesNow); | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Reports whether or not a given service is | ||
| 89 | * currently available from this context. | ||
| 90 | * @param serviceClass the service in question | ||
| 91 | * @return true if the service is available | ||
| 92 | */ | ||
| 93 | boolean hasService(Class serviceClass); | ||
| 94 | |||
| 95 | /** | ||
| 96 | * A <code>BeanContextChild</code>, or any arbitrary object | ||
| 97 | * associated with a <code>BeanContextChild</code>, may obtain | ||
| 98 | * a reference to a currently registered service from its | ||
| 99 | * nesting <code>BeanContextServices</code> | ||
| 100 | * via invocation of this method. When invoked, this method | ||
| 101 | * gets the service by calling the getService() method on the | ||
| 102 | * underlying <code>BeanContextServiceProvider</code>. | ||
| 103 | * @param child the <code>BeanContextChild</code> | ||
| 104 | * associated with this request | ||
| 105 | * @param requestor the object requesting the service | ||
| 106 | * @param serviceClass class of the requested service | ||
| 107 | * @param serviceSelector the service dependent parameter | ||
| 108 | * @param bcsrl the | ||
| 109 | * <code>BeanContextServiceRevokedListener</code> to notify | ||
| 110 | * if the service should later become revoked | ||
| 111 | * @throws TooManyListenersException | ||
| 112 | * @return a reference to this context's named | ||
| 113 | * Service as requested or <code>null</code> | ||
| 114 | */ | ||
| 115 | Object getService(BeanContextChild child, Object requestor, Class serviceClass, Object serviceSelector, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException; | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Releases a <code>BeanContextChild</code>'s | ||
| 119 | * (or any arbitrary object associated with a BeanContextChild) | ||
| 120 | * reference to the specified service by calling releaseService() | ||
| 121 | * on the underlying <code>BeanContextServiceProvider</code>. | ||
| 122 | * @param child the <code>BeanContextChild</code> | ||
| 123 | * @param requestor the requestor | ||
| 124 | * @param service the service | ||
| 125 | */ | ||
| 126 | void releaseService(BeanContextChild child, Object requestor, Object service); | ||
| 127 | |||
| 128 | /** | ||
| 129 | * Gets the currently available services for this context. | ||
| 130 | * @return an <code>Iterator</code> consisting of the | ||
| 131 | * currently available services | ||
| 132 | */ | ||
| 133 | Iterator getCurrentServiceClasses(); | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Gets the list of service dependent service parameters | ||
| 137 | * (Service Selectors) for the specified service, by | ||
| 138 | * calling getCurrentServiceSelectors() on the | ||
| 139 | * underlying BeanContextServiceProvider. | ||
| 140 | * @param serviceClass the specified service | ||
| 141 | * @return the currently available service selectors | ||
| 142 | * for the named serviceClass | ||
| 143 | */ | ||
| 144 | Iterator getCurrentServiceSelectors(Class serviceClass); | ||
| 145 | |||
| 146 | /** | ||
| 147 | * Adds a <code>BeanContextServicesListener</code> to this BeanContext | ||
| 148 | * @param bcsl the <code>BeanContextServicesListener</code> to add | ||
| 149 | */ | ||
| 150 | void addBeanContextServicesListener(BeanContextServicesListener bcsl); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Removes a <code>BeanContextServicesListener</code> | ||
| 154 | * from this <code>BeanContext</code> | ||
| 155 | * @param bcsl the <code>BeanContextServicesListener</code> | ||
| 156 | * to remove from this context | ||
| 157 | */ | ||
| 158 | void removeBeanContextServicesListener(BeanContextServicesListener bcsl); | ||
| 159 | } | ||
Binary file not shown.
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.beans.beancontext.BeanContextServiceAvailableEvent; | ||
| 29 | import java.beans.beancontext.BeanContextServiceRevokedEvent; | ||
| 30 | import java.beans.beancontext.BeanContextServiceRevokedListener; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * The listener interface for receiving | ||
| 34 | * <code>BeanContextServiceAvailableEvent</code> objects. | ||
| 35 | * A class that is interested in processing a | ||
| 36 | * <code>BeanContextServiceAvailableEvent</code> implements this interface. | ||
| 37 | */ | ||
| 38 | public interface BeanContextServicesListener extends BeanContextServiceRevokedListener { | ||
| 39 | |||
| 40 | /** | ||
| 41 | * The service named has been registered. getService requests for | ||
| 42 | * this service may now be made. | ||
| 43 | * @param bcsae the <code>BeanContextServiceAvailableEvent</code> | ||
| 44 | */ | ||
| 45 | void serviceAvailable(BeanContextServiceAvailableEvent bcsae); | ||
| 46 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,1238 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.util.ArrayList; | ||
| 29 | import java.util.Collection; | ||
| 30 | import java.util.HashMap; | ||
| 31 | import java.util.HashSet; | ||
| 32 | import java.util.Iterator; | ||
| 33 | import java.util.Map; | ||
| 34 | import java.util.Map.Entry; | ||
| 35 | |||
| 36 | import java.io.IOException; | ||
| 37 | import java.io.ObjectInputStream; | ||
| 38 | import java.io.ObjectOutputStream; | ||
| 39 | import java.io.Serializable; | ||
| 40 | |||
| 41 | import java.util.TooManyListenersException; | ||
| 42 | |||
| 43 | import java.util.Locale; | ||
| 44 | |||
| 45 | /** | ||
| 46 | * <p> | ||
| 47 | * This helper class provides a utility implementation of the | ||
| 48 | * java.beans.beancontext.BeanContextServices interface. | ||
| 49 | * </p> | ||
| 50 | * <p> | ||
| 51 | * Since this class directly implements the BeanContextServices interface, | ||
| 52 | * the class can, and is intended to be used either by subclassing this | ||
| 53 | * implementation, or via delegation of an instance of this class | ||
| 54 | * from another through the BeanContextProxy interface. | ||
| 55 | * </p> | ||
| 56 | * | ||
| 57 | * @author Laurence P. G. Cable | ||
| 58 | * @since 1.2 | ||
| 59 | */ | ||
| 60 | |||
| 61 | public class BeanContextServicesSupport extends BeanContextSupport | ||
| 62 | implements BeanContextServices { | ||
| 63 | private static final long serialVersionUID = -8494482757288719206L; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * <p> | ||
| 67 | * Construct a BeanContextServicesSupport instance | ||
| 68 | * </p> | ||
| 69 | * | ||
| 70 | * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer | ||
| 71 | * @param lcle The current Locale for this BeanContext. | ||
| 72 | * @param dTime The initial state, true if in design mode, false if runtime. | ||
| 73 | * @param visible The initial visibility. | ||
| 74 | * | ||
| 75 | */ | ||
| 76 | |||
| 77 | public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dTime, boolean visible) { | ||
| 78 | super(peer, lcle, dTime, visible); | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Create an instance using the specified Locale and design mode. | ||
| 83 | * | ||
| 84 | * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer | ||
| 85 | * @param lcle The current Locale for this BeanContext. | ||
| 86 | * @param dtime The initial state, true if in design mode, false if runtime. | ||
| 87 | */ | ||
| 88 | |||
| 89 | public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dtime) { | ||
| 90 | this (peer, lcle, dtime, true); | ||
| 91 | } | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Create an instance using the specified locale | ||
| 95 | * | ||
| 96 | * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer | ||
| 97 | * @param lcle The current Locale for this BeanContext. | ||
| 98 | */ | ||
| 99 | |||
| 100 | public BeanContextServicesSupport(BeanContextServices peer, Locale lcle) { | ||
| 101 | this (peer, lcle, false, true); | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Create an instance with a peer | ||
| 106 | * | ||
| 107 | * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer | ||
| 108 | */ | ||
| 109 | |||
| 110 | public BeanContextServicesSupport(BeanContextServices peer) { | ||
| 111 | this (peer, null, false, true); | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Create an instance that is not a delegate of another object | ||
| 116 | */ | ||
| 117 | |||
| 118 | public BeanContextServicesSupport() { | ||
| 119 | this (null, null, false, true); | ||
| 120 | } | ||
| 121 | |||
| 122 | /** | ||
| 123 | * called by BeanContextSupport superclass during construction and | ||
| 124 | * deserialization to initialize subclass transient state. | ||
| 125 | * | ||
| 126 | * subclasses may envelope this method, but should not override it or | ||
| 127 | * call it directly. | ||
| 128 | */ | ||
| 129 | |||
| 130 | public void initialize() { | ||
| 131 | super.initialize(); | ||
| 132 | |||
| 133 | services = new HashMap(serializable + 1); | ||
| 134 | bcsListeners = new ArrayList(1); | ||
| 135 | } | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Gets the <tt>BeanContextServices</tt> associated with this | ||
| 139 | * <tt>BeanContextServicesSupport</tt>. | ||
| 140 | * | ||
| 141 | * @return the instance of <tt>BeanContext</tt> | ||
| 142 | * this object is providing the implementation for. | ||
| 143 | */ | ||
| 144 | public BeanContextServices getBeanContextServicesPeer() { | ||
| 145 | return (BeanContextServices)getBeanContextChildPeer(); | ||
| 146 | } | ||
| 147 | |||
| 148 | /************************************************************************/ | ||
| 149 | |||
| 150 | /* | ||
| 151 | * protected nested class containing per child information, an instance | ||
| 152 | * of which is associated with each child in the "children" hashtable. | ||
| 153 | * subclasses can extend this class to include their own per-child state. | ||
| 154 | * | ||
| 155 | * Note that this 'value' is serialized with the corresponding child 'key' | ||
| 156 | * when the BeanContextSupport is serialized. | ||
| 157 | */ | ||
| 158 | |||
| 159 | protected class BCSSChild extends BeanContextSupport.BCSChild { | ||
| 160 | |||
| 161 | private static final long serialVersionUID = -3263851306889194873L; | ||
| 162 | |||
| 163 | /* | ||
| 164 | * private nested class to map serviceClass to Provider and requestors | ||
| 165 | * listeners. | ||
| 166 | */ | ||
| 167 | |||
| 168 | class BCSSCServiceClassRef { | ||
| 169 | |||
| 170 | // create an instance of a service ref | ||
| 171 | |||
| 172 | BCSSCServiceClassRef(Class sc, BeanContextServiceProvider bcsp, boolean delegated) { | ||
| 173 | super(); | ||
| 174 | |||
| 175 | serviceClass = sc; | ||
| 176 | |||
| 177 | if (delegated) | ||
| 178 | delegateProvider = bcsp; | ||
| 179 | else | ||
| 180 | serviceProvider = bcsp; | ||
| 181 | } | ||
| 182 | |||
| 183 | // add a requestor and assoc listener | ||
| 184 | |||
| 185 | void addRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException { | ||
| 186 | BeanContextServiceRevokedListener cbcsrl = (BeanContextServiceRevokedListener)requestors.get(requestor); | ||
| 187 | |||
| 188 | if (cbcsrl != null && !cbcsrl.equals(bcsrl)) | ||
| 189 | throw new TooManyListenersException(); | ||
| 190 | |||
| 191 | requestors.put(requestor, bcsrl); | ||
| 192 | } | ||
| 193 | |||
| 194 | // remove a requestor | ||
| 195 | |||
| 196 | void removeRequestor(Object requestor) { | ||
| 197 | requestors.remove(requestor); | ||
| 198 | } | ||
| 199 | |||
| 200 | // check a requestors listener | ||
| 201 | |||
| 202 | void verifyRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException { | ||
| 203 | BeanContextServiceRevokedListener cbcsrl = (BeanContextServiceRevokedListener)requestors.get(requestor); | ||
| 204 | |||
| 205 | if (cbcsrl != null && !cbcsrl.equals(bcsrl)) | ||
| 206 | throw new TooManyListenersException(); | ||
| 207 | } | ||
| 208 | |||
| 209 | void verifyAndMaybeSetProvider(BeanContextServiceProvider bcsp, boolean isDelegated) { | ||
| 210 | BeanContextServiceProvider current; | ||
| 211 | |||
| 212 | if (isDelegated) { // the provider is delegated | ||
| 213 | current = delegateProvider; | ||
| 214 | |||
| 215 | if (current == null || bcsp == null) { | ||
| 216 | delegateProvider = bcsp; | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | } else { // the provider is registered with this BCS | ||
| 220 | current = serviceProvider; | ||
| 221 | |||
| 222 | if (current == null || bcsp == null) { | ||
| 223 | serviceProvider = bcsp; | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | if (!current.equals(bcsp)) | ||
| 229 | throw new UnsupportedOperationException("existing service reference obtained from different BeanContextServiceProvider not supported"); | ||
| 230 | |||
| 231 | } | ||
| 232 | |||
| 233 | Iterator cloneOfEntries() { | ||
| 234 | return ((HashMap)requestors.clone()).entrySet().iterator(); | ||
| 235 | } | ||
| 236 | |||
| 237 | Iterator entries() { return requestors.entrySet().iterator(); } | ||
| 238 | |||
| 239 | boolean isEmpty() { return requestors.isEmpty(); } | ||
| 240 | |||
| 241 | Class getServiceClass() { return serviceClass; } | ||
| 242 | |||
| 243 | BeanContextServiceProvider getServiceProvider() { | ||
| 244 | return serviceProvider; | ||
| 245 | } | ||
| 246 | |||
| 247 | BeanContextServiceProvider getDelegateProvider() { | ||
| 248 | return delegateProvider; | ||
| 249 | } | ||
| 250 | |||
| 251 | boolean isDelegated() { return delegateProvider != null; } | ||
| 252 | |||
| 253 | void addRef(boolean delegated) { | ||
| 254 | if (delegated) { | ||
| 255 | delegateRefs++; | ||
| 256 | } else { | ||
| 257 | serviceRefs++; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | |||
| 262 | void releaseRef(boolean delegated) { | ||
| 263 | if (delegated) { | ||
| 264 | if (--delegateRefs == 0) { | ||
| 265 | delegateProvider = null; | ||
| 266 | } | ||
| 267 | } else { | ||
| 268 | if (--serviceRefs <= 0) { | ||
| 269 | serviceProvider = null; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | int getRefs() { return serviceRefs + delegateRefs; } | ||
| 275 | |||
| 276 | int getDelegateRefs() { return delegateRefs; } | ||
| 277 | |||
| 278 | int getServiceRefs() { return serviceRefs; } | ||
| 279 | |||
| 280 | /* | ||
| 281 | * fields | ||
| 282 | */ | ||
| 283 | |||
| 284 | Class serviceClass; | ||
| 285 | |||
| 286 | BeanContextServiceProvider serviceProvider; | ||
| 287 | int serviceRefs; | ||
| 288 | |||
| 289 | BeanContextServiceProvider delegateProvider; // proxy | ||
| 290 | int delegateRefs; | ||
| 291 | |||
| 292 | HashMap requestors = new HashMap(1); | ||
| 293 | } | ||
| 294 | |||
| 295 | /* | ||
| 296 | * per service reference info ... | ||
| 297 | */ | ||
| 298 | |||
| 299 | class BCSSCServiceRef { | ||
| 300 | BCSSCServiceRef(BCSSCServiceClassRef scref, boolean isDelegated) { | ||
| 301 | serviceClassRef = scref; | ||
| 302 | delegated = isDelegated; | ||
| 303 | } | ||
| 304 | |||
| 305 | void addRef() { refCnt++; } | ||
| 306 | int release() { return --refCnt; } | ||
| 307 | |||
| 308 | BCSSCServiceClassRef getServiceClassRef() { return serviceClassRef; } | ||
| 309 | |||
| 310 | boolean isDelegated() { return delegated; } | ||
| 311 | |||
| 312 | /* | ||
| 313 | * fields | ||
| 314 | */ | ||
| 315 | |||
| 316 | BCSSCServiceClassRef serviceClassRef; | ||
| 317 | int refCnt = 1; | ||
| 318 | boolean delegated = false; | ||
| 319 | } | ||
| 320 | |||
| 321 | BCSSChild(Object bcc, Object peer) { super(bcc, peer); } | ||
| 322 | |||
| 323 | // note usage of service per requestor, per service | ||
| 324 | |||
| 325 | synchronized void usingService(Object requestor, Object service, Class serviceClass, BeanContextServiceProvider bcsp, boolean isDelegated, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException, UnsupportedOperationException { | ||
| 326 | |||
| 327 | // first, process mapping from serviceClass to requestor(s) | ||
| 328 | |||
| 329 | BCSSCServiceClassRef serviceClassRef = null; | ||
| 330 | |||
| 331 | if (serviceClasses == null) | ||
| 332 | serviceClasses = new HashMap(1); | ||
| 333 | else | ||
| 334 | serviceClassRef = (BCSSCServiceClassRef)serviceClasses.get(serviceClass); | ||
| 335 | |||
| 336 | if (serviceClassRef == null) { // new service being used ... | ||
| 337 | serviceClassRef = new BCSSCServiceClassRef(serviceClass, bcsp, isDelegated); | ||
| 338 | serviceClasses.put(serviceClass, serviceClassRef); | ||
| 339 | |||
| 340 | } else { // existing service ... | ||
| 341 | serviceClassRef.verifyAndMaybeSetProvider(bcsp, isDelegated); // throws | ||
| 342 | serviceClassRef.verifyRequestor(requestor, bcsrl); // throws | ||
| 343 | } | ||
| 344 | |||
| 345 | serviceClassRef.addRequestor(requestor, bcsrl); | ||
| 346 | serviceClassRef.addRef(isDelegated); | ||
| 347 | |||
| 348 | // now handle mapping from requestor to service(s) | ||
| 349 | |||
| 350 | BCSSCServiceRef serviceRef = null; | ||
| 351 | Map services = null; | ||
| 352 | |||
| 353 | if (serviceRequestors == null) { | ||
| 354 | serviceRequestors = new HashMap(1); | ||
| 355 | } else { | ||
| 356 | services = (Map)serviceRequestors.get(requestor); | ||
| 357 | } | ||
| 358 | |||
| 359 | if (services == null) { | ||
| 360 | services = new HashMap(1); | ||
| 361 | |||
| 362 | serviceRequestors.put(requestor, services); | ||
| 363 | } else | ||
| 364 | serviceRef = (BCSSCServiceRef)services.get(service); | ||
| 365 | |||
| 366 | if (serviceRef == null) { | ||
| 367 | serviceRef = new BCSSCServiceRef(serviceClassRef, isDelegated); | ||
| 368 | |||
| 369 | services.put(service, serviceRef); | ||
| 370 | } else { | ||
| 371 | serviceRef.addRef(); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | // release a service reference | ||
| 376 | |||
| 377 | synchronized void releaseService(Object requestor, Object service) { | ||
| 378 | if (serviceRequestors == null) return; | ||
| 379 | |||
| 380 | Map services = (Map)serviceRequestors.get(requestor); | ||
| 381 | |||
| 382 | if (services == null) return; // oops its not there anymore! | ||
| 383 | |||
| 384 | BCSSCServiceRef serviceRef = (BCSSCServiceRef)services.get(service); | ||
| 385 | |||
| 386 | if (serviceRef == null) return; // oops its not there anymore! | ||
| 387 | |||
| 388 | BCSSCServiceClassRef serviceClassRef = serviceRef.getServiceClassRef(); | ||
| 389 | boolean isDelegated = serviceRef.isDelegated(); | ||
| 390 | BeanContextServiceProvider bcsp = isDelegated ? serviceClassRef.getDelegateProvider() : serviceClassRef.getServiceProvider(); | ||
| 391 | |||
| 392 | bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service); | ||
| 393 | |||
| 394 | serviceClassRef.releaseRef(isDelegated); | ||
| 395 | serviceClassRef.removeRequestor(requestor); | ||
| 396 | |||
| 397 | if (serviceRef.release() == 0) { | ||
| 398 | |||
| 399 | services.remove(service); | ||
| 400 | |||
| 401 | if (services.isEmpty()) { | ||
| 402 | serviceRequestors.remove(requestor); | ||
| 403 | serviceClassRef.removeRequestor(requestor); | ||
| 404 | } | ||
| 405 | |||
| 406 | if (serviceRequestors.isEmpty()) { | ||
| 407 | serviceRequestors = null; | ||
| 408 | } | ||
| 409 | |||
| 410 | if (serviceClassRef.isEmpty()) { | ||
| 411 | serviceClasses.remove(serviceClassRef.getServiceClass()); | ||
| 412 | } | ||
| 413 | |||
| 414 | if (serviceClasses.isEmpty()) | ||
| 415 | serviceClasses = null; | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | // revoke a service | ||
| 420 | |||
| 421 | synchronized void revokeService(Class serviceClass, boolean isDelegated, boolean revokeNow) { | ||
| 422 | if (serviceClasses == null) return; | ||
| 423 | |||
| 424 | BCSSCServiceClassRef serviceClassRef = (BCSSCServiceClassRef)serviceClasses.get(serviceClass); | ||
| 425 | |||
| 426 | if (serviceClassRef == null) return; | ||
| 427 | |||
| 428 | Iterator i = serviceClassRef.cloneOfEntries(); | ||
| 429 | |||
| 430 | BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClass, revokeNow); | ||
| 431 | boolean noMoreRefs = false; | ||
| 432 | |||
| 433 | while (i.hasNext() && serviceRequestors != null) { | ||
| 434 | Map.Entry entry = (Map.Entry)i.next(); | ||
| 435 | BeanContextServiceRevokedListener listener = (BeanContextServiceRevokedListener)entry.getValue(); | ||
| 436 | |||
| 437 | if (revokeNow) { | ||
| 438 | Object requestor = entry.getKey(); | ||
| 439 | Map services = (Map)serviceRequestors.get(requestor); | ||
| 440 | |||
| 441 | if (services != null) { | ||
| 442 | Iterator i1 = services.entrySet().iterator(); | ||
| 443 | |||
| 444 | while (i1.hasNext()) { | ||
| 445 | Map.Entry tmp = (Map.Entry)i1.next(); | ||
| 446 | |||
| 447 | BCSSCServiceRef serviceRef = (BCSSCServiceRef)tmp.getValue(); | ||
| 448 | if (serviceRef.getServiceClassRef().equals(serviceClassRef) && isDelegated == serviceRef.isDelegated()) { | ||
| 449 | i1.remove(); | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | if (noMoreRefs = services.isEmpty()) { | ||
| 454 | serviceRequestors.remove(requestor); | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | if (noMoreRefs) serviceClassRef.removeRequestor(requestor); | ||
| 459 | } | ||
| 460 | |||
| 461 | listener.serviceRevoked(bcsre); | ||
| 462 | } | ||
| 463 | |||
| 464 | if (revokeNow && serviceClasses != null) { | ||
| 465 | if (serviceClassRef.isEmpty()) | ||
| 466 | serviceClasses.remove(serviceClass); | ||
| 467 | |||
| 468 | if (serviceClasses.isEmpty()) | ||
| 469 | serviceClasses = null; | ||
| 470 | } | ||
| 471 | |||
| 472 | if (serviceRequestors != null && serviceRequestors.isEmpty()) | ||
| 473 | serviceRequestors = null; | ||
| 474 | } | ||
| 475 | |||
| 476 | // release all references for this child since it has been unnested. | ||
| 477 | |||
| 478 | void cleanupReferences() { | ||
| 479 | |||
| 480 | if (serviceRequestors == null) return; | ||
| 481 | |||
| 482 | Iterator requestors = serviceRequestors.entrySet().iterator(); | ||
| 483 | |||
| 484 | while(requestors.hasNext()) { | ||
| 485 | Map.Entry tmp = (Map.Entry)requestors.next(); | ||
| 486 | Object requestor = tmp.getKey(); | ||
| 487 | Iterator services = ((Map)tmp.getValue()).entrySet().iterator(); | ||
| 488 | |||
| 489 | requestors.remove(); | ||
| 490 | |||
| 491 | while (services.hasNext()) { | ||
| 492 | Map.Entry entry = (Map.Entry)services.next(); | ||
| 493 | Object service = entry.getKey(); | ||
| 494 | BCSSCServiceRef sref = (BCSSCServiceRef)entry.getValue(); | ||
| 495 | |||
| 496 | BCSSCServiceClassRef scref = sref.getServiceClassRef(); | ||
| 497 | |||
| 498 | BeanContextServiceProvider bcsp = sref.isDelegated() ? scref.getDelegateProvider() : scref.getServiceProvider(); | ||
| 499 | |||
| 500 | scref.removeRequestor(requestor); | ||
| 501 | services.remove(); | ||
| 502 | |||
| 503 | while (sref.release() >= 0) { | ||
| 504 | bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service); | ||
| 505 | } | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | serviceRequestors = null; | ||
| 510 | serviceClasses = null; | ||
| 511 | } | ||
| 512 | |||
| 513 | void revokeAllDelegatedServicesNow() { | ||
| 514 | if (serviceClasses == null) return; | ||
| 515 | |||
| 516 | Iterator serviceClassRefs = | ||
| 517 | new HashSet(serviceClasses.values()).iterator(); | ||
| 518 | |||
| 519 | while (serviceClassRefs.hasNext()) { | ||
| 520 | BCSSCServiceClassRef serviceClassRef = (BCSSCServiceClassRef)serviceClassRefs.next(); | ||
| 521 | |||
| 522 | if (!serviceClassRef.isDelegated()) continue; | ||
| 523 | |||
| 524 | Iterator i = serviceClassRef.cloneOfEntries(); | ||
| 525 | BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClassRef.getServiceClass(), true); | ||
| 526 | boolean noMoreRefs = false; | ||
| 527 | |||
| 528 | while (i.hasNext()) { | ||
| 529 | Map.Entry entry = (Map.Entry)i.next(); | ||
| 530 | BeanContextServiceRevokedListener listener = (BeanContextServiceRevokedListener)entry.getValue(); | ||
| 531 | |||
| 532 | Object requestor = entry.getKey(); | ||
| 533 | Map services = (Map)serviceRequestors.get(requestor); | ||
| 534 | |||
| 535 | if (services != null) { | ||
| 536 | Iterator i1 = services.entrySet().iterator(); | ||
| 537 | |||
| 538 | while (i1.hasNext()) { | ||
| 539 | Map.Entry tmp = (Map.Entry)i1.next(); | ||
| 540 | |||
| 541 | BCSSCServiceRef serviceRef = (BCSSCServiceRef)tmp.getValue(); | ||
| 542 | if (serviceRef.getServiceClassRef().equals(serviceClassRef) && serviceRef.isDelegated()) { | ||
| 543 | i1.remove(); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | if (noMoreRefs = services.isEmpty()) { | ||
| 548 | serviceRequestors.remove(requestor); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | if (noMoreRefs) serviceClassRef.removeRequestor(requestor); | ||
| 553 | |||
| 554 | listener.serviceRevoked(bcsre); | ||
| 555 | |||
| 556 | if (serviceClassRef.isEmpty()) | ||
| 557 | serviceClasses.remove(serviceClassRef.getServiceClass()); | ||
| 558 | } | ||
| 559 | } | ||
| 560 | |||
| 561 | if (serviceClasses.isEmpty()) serviceClasses = null; | ||
| 562 | |||
| 563 | if (serviceRequestors != null && serviceRequestors.isEmpty()) | ||
| 564 | serviceRequestors = null; | ||
| 565 | } | ||
| 566 | |||
| 567 | /* | ||
| 568 | * fields | ||
| 569 | */ | ||
| 570 | |||
| 571 | private transient HashMap serviceClasses; | ||
| 572 | private transient HashMap serviceRequestors; | ||
| 573 | } | ||
| 574 | |||
| 575 | /** | ||
| 576 | * <p> | ||
| 577 | * Subclasses can override this method to insert their own subclass | ||
| 578 | * of Child without having to override add() or the other Collection | ||
| 579 | * methods that add children to the set. | ||
| 580 | * </p> | ||
| 581 | * | ||
| 582 | * @param targetChild the child to create the Child on behalf of | ||
| 583 | * @param peer the peer if the targetChild and peer are related by BeanContextProxy | ||
| 584 | */ | ||
| 585 | |||
| 586 | protected BCSChild createBCSChild(Object targetChild, Object peer) { | ||
| 587 | return new BCSSChild(targetChild, peer); | ||
| 588 | } | ||
| 589 | |||
| 590 | /************************************************************************/ | ||
| 591 | |||
| 592 | /** | ||
| 593 | * subclasses may subclass this nested class to add behaviors for | ||
| 594 | * each BeanContextServicesProvider. | ||
| 595 | */ | ||
| 596 | |||
| 597 | protected static class BCSSServiceProvider implements Serializable { | ||
| 598 | private static final long serialVersionUID = 861278251667444782L; | ||
| 599 | |||
| 600 | BCSSServiceProvider(Class sc, BeanContextServiceProvider bcsp) { | ||
| 601 | super(); | ||
| 602 | |||
| 603 | serviceProvider = bcsp; | ||
| 604 | } | ||
| 605 | |||
| 606 | protected BeanContextServiceProvider getServiceProvider() { | ||
| 607 | return serviceProvider; | ||
| 608 | } | ||
| 609 | |||
| 610 | /* | ||
| 611 | * fields | ||
| 612 | */ | ||
| 613 | |||
| 614 | protected BeanContextServiceProvider serviceProvider; | ||
| 615 | } | ||
| 616 | |||
| 617 | /** | ||
| 618 | * subclasses can override this method to create new subclasses of | ||
| 619 | * BCSSServiceProvider without having to overrride addService() in | ||
| 620 | * order to instantiate. | ||
| 621 | */ | ||
| 622 | |||
| 623 | protected BCSSServiceProvider createBCSSServiceProvider(Class sc, BeanContextServiceProvider bcsp) { | ||
| 624 | return new BCSSServiceProvider(sc, bcsp); | ||
| 625 | } | ||
| 626 | |||
| 627 | /************************************************************************/ | ||
| 628 | |||
| 629 | /** | ||
| 630 | * add a BeanContextServicesListener | ||
| 631 | * | ||
| 632 | * @throws NullPointerException | ||
| 633 | */ | ||
| 634 | |||
| 635 | public void addBeanContextServicesListener(BeanContextServicesListener bcsl) { | ||
| 636 | if (bcsl == null) throw new NullPointerException("bcsl"); | ||
| 637 | |||
| 638 | synchronized(bcsListeners) { | ||
| 639 | if (bcsListeners.contains(bcsl)) | ||
| 640 | return; | ||
| 641 | else | ||
| 642 | bcsListeners.add(bcsl); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | |||
| 646 | /** | ||
| 647 | * remove a BeanContextServicesListener | ||
| 648 | */ | ||
| 649 | |||
| 650 | public void removeBeanContextServicesListener(BeanContextServicesListener bcsl) { | ||
| 651 | if (bcsl == null) throw new NullPointerException("bcsl"); | ||
| 652 | |||
| 653 | synchronized(bcsListeners) { | ||
| 654 | if (!bcsListeners.contains(bcsl)) | ||
| 655 | return; | ||
| 656 | else | ||
| 657 | bcsListeners.remove(bcsl); | ||
| 658 | } | ||
| 659 | } | ||
| 660 | |||
| 661 | /** | ||
| 662 | * add a service | ||
| 663 | */ | ||
| 664 | |||
| 665 | public boolean addService(Class serviceClass, BeanContextServiceProvider bcsp) { | ||
| 666 | return addService(serviceClass, bcsp, true); | ||
| 667 | } | ||
| 668 | |||
| 669 | /** | ||
| 670 | * add a service | ||
| 671 | */ | ||
| 672 | |||
| 673 | protected boolean addService(Class serviceClass, BeanContextServiceProvider bcsp, boolean fireEvent) { | ||
| 674 | |||
| 675 | if (serviceClass == null) throw new NullPointerException("serviceClass"); | ||
| 676 | if (bcsp == null) throw new NullPointerException("bcsp"); | ||
| 677 | |||
| 678 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 679 | if (services.containsKey(serviceClass)) | ||
| 680 | return false; | ||
| 681 | else { | ||
| 682 | services.put(serviceClass, createBCSSServiceProvider(serviceClass, bcsp)); | ||
| 683 | |||
| 684 | if (bcsp instanceof Serializable) serializable++; | ||
| 685 | |||
| 686 | if (!fireEvent) return true; | ||
| 687 | |||
| 688 | |||
| 689 | BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass); | ||
| 690 | |||
| 691 | fireServiceAdded(bcssae); | ||
| 692 | |||
| 693 | synchronized(children) { | ||
| 694 | Iterator i = children.keySet().iterator(); | ||
| 695 | |||
| 696 | while (i.hasNext()) { | ||
| 697 | Object c = i.next(); | ||
| 698 | |||
| 699 | if (c instanceof BeanContextServices) { | ||
| 700 | ((BeanContextServicesListener)c).serviceAvailable(bcssae); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | } | ||
| 704 | |||
| 705 | return true; | ||
| 706 | } | ||
| 707 | } | ||
| 708 | } | ||
| 709 | |||
| 710 | /** | ||
| 711 | * remove a service | ||
| 712 | */ | ||
| 713 | |||
| 714 | public void revokeService(Class serviceClass, BeanContextServiceProvider bcsp, boolean revokeCurrentServicesNow) { | ||
| 715 | |||
| 716 | if (serviceClass == null) throw new NullPointerException("serviceClass"); | ||
| 717 | if (bcsp == null) throw new NullPointerException("bcsp"); | ||
| 718 | |||
| 719 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 720 | if (!services.containsKey(serviceClass)) return; | ||
| 721 | |||
| 722 | BCSSServiceProvider bcsssp = (BCSSServiceProvider)services.get(serviceClass); | ||
| 723 | |||
| 724 | if (!bcsssp.getServiceProvider().equals(bcsp)) | ||
| 725 | throw new IllegalArgumentException("service provider mismatch"); | ||
| 726 | |||
| 727 | services.remove(serviceClass); | ||
| 728 | |||
| 729 | if (bcsp instanceof Serializable) serializable--; | ||
| 730 | |||
| 731 | Iterator i = bcsChildren(); // get the BCSChild values. | ||
| 732 | |||
| 733 | while (i.hasNext()) { | ||
| 734 | ((BCSSChild)i.next()).revokeService(serviceClass, false, revokeCurrentServicesNow); | ||
| 735 | } | ||
| 736 | |||
| 737 | fireServiceRevoked(serviceClass, revokeCurrentServicesNow); | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 741 | /** | ||
| 742 | * has a service, which may be delegated | ||
| 743 | */ | ||
| 744 | |||
| 745 | public synchronized boolean hasService(Class serviceClass) { | ||
| 746 | if (serviceClass == null) throw new NullPointerException("serviceClass"); | ||
| 747 | |||
| 748 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 749 | if (services.containsKey(serviceClass)) return true; | ||
| 750 | |||
| 751 | BeanContextServices bcs = null; | ||
| 752 | |||
| 753 | try { | ||
| 754 | bcs = (BeanContextServices)getBeanContext(); | ||
| 755 | } catch (ClassCastException cce) { | ||
| 756 | return false; | ||
| 757 | } | ||
| 758 | |||
| 759 | return bcs == null ? false : bcs.hasService(serviceClass); | ||
| 760 | } | ||
| 761 | } | ||
| 762 | |||
| 763 | /************************************************************************/ | ||
| 764 | |||
| 765 | /* | ||
| 766 | * a nested subclass used to represent a proxy for serviceClasses delegated | ||
| 767 | * to an enclosing BeanContext. | ||
| 768 | */ | ||
| 769 | |||
| 770 | protected class BCSSProxyServiceProvider implements BeanContextServiceProvider, BeanContextServiceRevokedListener { | ||
| 771 | |||
| 772 | BCSSProxyServiceProvider(BeanContextServices bcs) { | ||
| 773 | super(); | ||
| 774 | |||
| 775 | nestingCtxt = bcs; | ||
| 776 | } | ||
| 777 | |||
| 778 | public Object getService(BeanContextServices bcs, Object requestor, Class serviceClass, Object serviceSelector) { | ||
| 779 | Object service = null; | ||
| 780 | |||
| 781 | try { | ||
| 782 | service = nestingCtxt.getService(bcs, requestor, serviceClass, serviceSelector, this); | ||
| 783 | } catch (TooManyListenersException tmle) { | ||
| 784 | return null; | ||
| 785 | } | ||
| 786 | |||
| 787 | return service; | ||
| 788 | } | ||
| 789 | |||
| 790 | public void releaseService(BeanContextServices bcs, Object requestor, Object service) { | ||
| 791 | nestingCtxt.releaseService(bcs, requestor, service); | ||
| 792 | } | ||
| 793 | |||
| 794 | public Iterator getCurrentServiceSelectors(BeanContextServices bcs, Class serviceClass) { | ||
| 795 | return nestingCtxt.getCurrentServiceSelectors(serviceClass); | ||
| 796 | } | ||
| 797 | |||
| 798 | public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) { | ||
| 799 | Iterator i = bcsChildren(); // get the BCSChild values. | ||
| 800 | |||
| 801 | while (i.hasNext()) { | ||
| 802 | ((BCSSChild)i.next()).revokeService(bcsre.getServiceClass(), true, bcsre.isCurrentServiceInvalidNow()); | ||
| 803 | } | ||
| 804 | } | ||
| 805 | |||
| 806 | /* | ||
| 807 | * fields | ||
| 808 | */ | ||
| 809 | |||
| 810 | private BeanContextServices nestingCtxt; | ||
| 811 | } | ||
| 812 | |||
| 813 | /************************************************************************/ | ||
| 814 | |||
| 815 | /** | ||
| 816 | * obtain a service which may be delegated | ||
| 817 | */ | ||
| 818 | |||
| 819 | public Object getService(BeanContextChild child, Object requestor, Class serviceClass, Object serviceSelector, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException { | ||
| 820 | if (child == null) throw new NullPointerException("child"); | ||
| 821 | if (serviceClass == null) throw new NullPointerException("serviceClass"); | ||
| 822 | if (requestor == null) throw new NullPointerException("requestor"); | ||
| 823 | if (bcsrl == null) throw new NullPointerException("bcsrl"); | ||
| 824 | |||
| 825 | Object service = null; | ||
| 826 | BCSSChild bcsc; | ||
| 827 | BeanContextServices bcssp = getBeanContextServicesPeer(); | ||
| 828 | |||
| 829 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 830 | synchronized(children) { bcsc = (BCSSChild)children.get(child); } | ||
| 831 | |||
| 832 | if (bcsc == null) throw new IllegalArgumentException("not a child of this context"); // not a child ... | ||
| 833 | |||
| 834 | BCSSServiceProvider bcsssp = (BCSSServiceProvider)services.get(serviceClass); | ||
| 835 | |||
| 836 | if (bcsssp != null) { | ||
| 837 | BeanContextServiceProvider bcsp = bcsssp.getServiceProvider(); | ||
| 838 | service = bcsp.getService(bcssp, requestor, serviceClass, serviceSelector); | ||
| 839 | if (service != null) { // do bookkeeping ... | ||
| 840 | try { | ||
| 841 | bcsc.usingService(requestor, service, serviceClass, bcsp, false, bcsrl); | ||
| 842 | } catch (TooManyListenersException tmle) { | ||
| 843 | bcsp.releaseService(bcssp, requestor, service); | ||
| 844 | throw tmle; | ||
| 845 | } catch (UnsupportedOperationException uope) { | ||
| 846 | bcsp.releaseService(bcssp, requestor, service); | ||
| 847 | throw uope; // unchecked rt exception | ||
| 848 | } | ||
| 849 | |||
| 850 | return service; | ||
| 851 | } | ||
| 852 | } | ||
| 853 | |||
| 854 | |||
| 855 | if (proxy != null) { | ||
| 856 | |||
| 857 | // try to delegate ... | ||
| 858 | |||
| 859 | service = proxy.getService(bcssp, requestor, serviceClass, serviceSelector); | ||
| 860 | |||
| 861 | if (service != null) { // do bookkeeping ... | ||
| 862 | try { | ||
| 863 | bcsc.usingService(requestor, service, serviceClass, proxy, true, bcsrl); | ||
| 864 | } catch (TooManyListenersException tmle) { | ||
| 865 | proxy.releaseService(bcssp, requestor, service); | ||
| 866 | throw tmle; | ||
| 867 | } catch (UnsupportedOperationException uope) { | ||
| 868 | proxy.releaseService(bcssp, requestor, service); | ||
| 869 | throw uope; // unchecked rt exception | ||
| 870 | } | ||
| 871 | |||
| 872 | return service; | ||
| 873 | } | ||
| 874 | } | ||
| 875 | } | ||
| 876 | |||
| 877 | return null; | ||
| 878 | } | ||
| 879 | |||
| 880 | /** | ||
| 881 | * release a service | ||
| 882 | */ | ||
| 883 | |||
| 884 | public void releaseService(BeanContextChild child, Object requestor, Object service) { | ||
| 885 | if (child == null) throw new NullPointerException("child"); | ||
| 886 | if (requestor == null) throw new NullPointerException("requestor"); | ||
| 887 | if (service == null) throw new NullPointerException("service"); | ||
| 888 | |||
| 889 | BCSSChild bcsc; | ||
| 890 | |||
| 891 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 892 | synchronized(children) { bcsc = (BCSSChild)children.get(child); } | ||
| 893 | |||
| 894 | if (bcsc != null) | ||
| 895 | bcsc.releaseService(requestor, service); | ||
| 896 | else | ||
| 897 | throw new IllegalArgumentException("child actual is not a child of this BeanContext"); | ||
| 898 | } | ||
| 899 | } | ||
| 900 | |||
| 901 | /** | ||
| 902 | * @return an iterator for all the currently registered service classes. | ||
| 903 | */ | ||
| 904 | |||
| 905 | public Iterator getCurrentServiceClasses() { | ||
| 906 | return new BCSIterator(services.keySet().iterator()); | ||
| 907 | } | ||
| 908 | |||
| 909 | /** | ||
| 910 | * @return an iterator for all the currently available service selectors | ||
| 911 | * (if any) available for the specified service. | ||
| 912 | */ | ||
| 913 | |||
| 914 | public Iterator getCurrentServiceSelectors(Class serviceClass) { | ||
| 915 | |||
| 916 | BCSSServiceProvider bcsssp = (BCSSServiceProvider)services.get(serviceClass); | ||
| 917 | |||
| 918 | return bcsssp != null ? new BCSIterator(bcsssp.getServiceProvider().getCurrentServiceSelectors(getBeanContextServicesPeer(), serviceClass)) : null; | ||
| 919 | } | ||
| 920 | |||
| 921 | /** | ||
| 922 | * BeanContextServicesListener callback, propagates event to all | ||
| 923 | * currently registered listeners and BeanContextServices children, | ||
| 924 | * if this BeanContextService does not already implement this service | ||
| 925 | * itself. | ||
| 926 | * | ||
| 927 | * subclasses may override or envelope this method to implement their | ||
| 928 | * own propagation semantics. | ||
| 929 | */ | ||
| 930 | |||
| 931 | public void serviceAvailable(BeanContextServiceAvailableEvent bcssae) { | ||
| 932 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 933 | if (services.containsKey(bcssae.getServiceClass())) return; | ||
| 934 | |||
| 935 | fireServiceAdded(bcssae); | ||
| 936 | |||
| 937 | Iterator i; | ||
| 938 | |||
| 939 | synchronized(children) { | ||
| 940 | i = children.keySet().iterator(); | ||
| 941 | } | ||
| 942 | |||
| 943 | while (i.hasNext()) { | ||
| 944 | Object c = i.next(); | ||
| 945 | |||
| 946 | if (c instanceof BeanContextServices) { | ||
| 947 | ((BeanContextServicesListener)c).serviceAvailable(bcssae); | ||
| 948 | } | ||
| 949 | } | ||
| 950 | } | ||
| 951 | } | ||
| 952 | |||
| 953 | /** | ||
| 954 | * BeanContextServicesListener callback, propagates event to all | ||
| 955 | * currently registered listeners and BeanContextServices children, | ||
| 956 | * if this BeanContextService does not already implement this service | ||
| 957 | * itself. | ||
| 958 | * | ||
| 959 | * subclasses may override or envelope this method to implement their | ||
| 960 | * own propagation semantics. | ||
| 961 | */ | ||
| 962 | |||
| 963 | public void serviceRevoked(BeanContextServiceRevokedEvent bcssre) { | ||
| 964 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 965 | if (services.containsKey(bcssre.getServiceClass())) return; | ||
| 966 | |||
| 967 | fireServiceRevoked(bcssre); | ||
| 968 | |||
| 969 | Iterator i; | ||
| 970 | |||
| 971 | synchronized(children) { | ||
| 972 | i = children.keySet().iterator(); | ||
| 973 | } | ||
| 974 | |||
| 975 | while (i.hasNext()) { | ||
| 976 | Object c = i.next(); | ||
| 977 | |||
| 978 | if (c instanceof BeanContextServices) { | ||
| 979 | ((BeanContextServicesListener)c).serviceRevoked(bcssre); | ||
| 980 | } | ||
| 981 | } | ||
| 982 | } | ||
| 983 | } | ||
| 984 | |||
| 985 | /** | ||
| 986 | * Gets the <tt>BeanContextServicesListener</tt> (if any) of the specified | ||
| 987 | * child. | ||
| 988 | * | ||
| 989 | * @param child the specified child | ||
| 990 | * @return the BeanContextServicesListener (if any) of the specified child | ||
| 991 | */ | ||
| 992 | protected static final BeanContextServicesListener getChildBeanContextServicesListener(Object child) { | ||
| 993 | try { | ||
| 994 | return (BeanContextServicesListener)child; | ||
| 995 | } catch (ClassCastException cce) { | ||
| 996 | return null; | ||
| 997 | } | ||
| 998 | } | ||
| 999 | |||
| 1000 | /** | ||
| 1001 | * called from superclass child removal operations after a child | ||
| 1002 | * has been successfully removed. called with child synchronized. | ||
| 1003 | * | ||
| 1004 | * This subclass uses this hook to immediately revoke any services | ||
| 1005 | * being used by this child if it is a BeanContextChild. | ||
| 1006 | * | ||
| 1007 | * subclasses may envelope this method in order to implement their | ||
| 1008 | * own child removal side-effects. | ||
| 1009 | */ | ||
| 1010 | |||
| 1011 | protected void childJustRemovedHook(Object child, BCSChild bcsc) { | ||
| 1012 | BCSSChild bcssc = (BCSSChild)bcsc; | ||
| 1013 | |||
| 1014 | bcssc.cleanupReferences(); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | /** | ||
| 1018 | * called from setBeanContext to notify a BeanContextChild | ||
| 1019 | * to release resources obtained from the nesting BeanContext. | ||
| 1020 | * | ||
| 1021 | * This method revokes any services obtained from its parent. | ||
| 1022 | * | ||
| 1023 | * subclasses may envelope this method to implement their own semantics. | ||
| 1024 | */ | ||
| 1025 | |||
| 1026 | protected synchronized void releaseBeanContextResources() { | ||
| 1027 | Object[] bcssc; | ||
| 1028 | |||
| 1029 | super.releaseBeanContextResources(); | ||
| 1030 | |||
| 1031 | synchronized(children) { | ||
| 1032 | if (children.isEmpty()) return; | ||
| 1033 | |||
| 1034 | bcssc = children.values().toArray(); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | |||
| 1038 | for (int i = 0; i < bcssc.length; i++) { | ||
| 1039 | ((BCSSChild)bcssc[i]).revokeAllDelegatedServicesNow(); | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | proxy = null; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | /** | ||
| 1046 | * called from setBeanContext to notify a BeanContextChild | ||
| 1047 | * to allocate resources obtained from the nesting BeanContext. | ||
| 1048 | * | ||
| 1049 | * subclasses may envelope this method to implement their own semantics. | ||
| 1050 | */ | ||
| 1051 | |||
| 1052 | protected synchronized void initializeBeanContextResources() { | ||
| 1053 | super.initializeBeanContextResources(); | ||
| 1054 | |||
| 1055 | BeanContext nbc = getBeanContext(); | ||
| 1056 | |||
| 1057 | if (nbc == null) return; | ||
| 1058 | |||
| 1059 | try { | ||
| 1060 | BeanContextServices bcs = (BeanContextServices)nbc; | ||
| 1061 | |||
| 1062 | proxy = new BCSSProxyServiceProvider(bcs); | ||
| 1063 | } catch (ClassCastException cce) { | ||
| 1064 | // do nothing ... | ||
| 1065 | } | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | /** | ||
| 1069 | * Fires a <tt>BeanContextServiceEvent</tt> notifying of a new service. | ||
| 1070 | */ | ||
| 1071 | protected final void fireServiceAdded(Class serviceClass) { | ||
| 1072 | BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass); | ||
| 1073 | |||
| 1074 | fireServiceAdded(bcssae); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | /** | ||
| 1078 | * Fires a <tt>BeanContextServiceAvailableEvent</tt> indicating that a new | ||
| 1079 | * service has become available. | ||
| 1080 | * | ||
| 1081 | * @param bcssae the <tt>BeanContextServiceAvailableEvent</tt> | ||
| 1082 | */ | ||
| 1083 | protected final void fireServiceAdded(BeanContextServiceAvailableEvent bcssae) { | ||
| 1084 | Object[] copy; | ||
| 1085 | |||
| 1086 | synchronized (bcsListeners) { copy = bcsListeners.toArray(); } | ||
| 1087 | |||
| 1088 | for (int i = 0; i < copy.length; i++) { | ||
| 1089 | ((BeanContextServicesListener)copy[i]).serviceAvailable(bcssae); | ||
| 1090 | } | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | /** | ||
| 1094 | * Fires a <tt>BeanContextServiceEvent</tt> notifying of a service being revoked. | ||
| 1095 | * | ||
| 1096 | * @param bcsre the <tt>BeanContextServiceRevokedEvent</tt> | ||
| 1097 | */ | ||
| 1098 | protected final void fireServiceRevoked(BeanContextServiceRevokedEvent bcsre) { | ||
| 1099 | Object[] copy; | ||
| 1100 | |||
| 1101 | synchronized (bcsListeners) { copy = bcsListeners.toArray(); } | ||
| 1102 | |||
| 1103 | for (int i = 0; i < copy.length; i++) { | ||
| 1104 | ((BeanContextServiceRevokedListener)copy[i]).serviceRevoked(bcsre); | ||
| 1105 | } | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | /** | ||
| 1109 | * Fires a <tt>BeanContextServiceRevokedEvent</tt> | ||
| 1110 | * indicating that a particular service is | ||
| 1111 | * no longer available. | ||
| 1112 | */ | ||
| 1113 | protected final void fireServiceRevoked(Class serviceClass, boolean revokeNow) { | ||
| 1114 | Object[] copy; | ||
| 1115 | BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(getBeanContextServicesPeer(), serviceClass, revokeNow); | ||
| 1116 | |||
| 1117 | synchronized (bcsListeners) { copy = bcsListeners.toArray(); } | ||
| 1118 | |||
| 1119 | for (int i = 0; i < copy.length; i++) { | ||
| 1120 | ((BeanContextServicesListener)copy[i]).serviceRevoked(bcsre); | ||
| 1121 | } | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | /** | ||
| 1125 | * called from BeanContextSupport writeObject before it serializes the | ||
| 1126 | * children ... | ||
| 1127 | * | ||
| 1128 | * This class will serialize any Serializable BeanContextServiceProviders | ||
| 1129 | * herein. | ||
| 1130 | * | ||
| 1131 | * subclasses may envelope this method to insert their own serialization | ||
| 1132 | * processing that has to occur prior to serialization of the children | ||
| 1133 | */ | ||
| 1134 | |||
| 1135 | protected synchronized void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException { | ||
| 1136 | |||
| 1137 | oos.writeInt(serializable); | ||
| 1138 | |||
| 1139 | if (serializable <= 0) return; | ||
| 1140 | |||
| 1141 | int count = 0; | ||
| 1142 | |||
| 1143 | Iterator i = services.entrySet().iterator(); | ||
| 1144 | |||
| 1145 | while (i.hasNext() && count < serializable) { | ||
| 1146 | Map.Entry entry = (Map.Entry)i.next(); | ||
| 1147 | BCSSServiceProvider bcsp = null; | ||
| 1148 | |||
| 1149 | try { | ||
| 1150 | bcsp = (BCSSServiceProvider)entry.getValue(); | ||
| 1151 | } catch (ClassCastException cce) { | ||
| 1152 | continue; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | if (bcsp.getServiceProvider() instanceof Serializable) { | ||
| 1156 | oos.writeObject(entry.getKey()); | ||
| 1157 | oos.writeObject(bcsp); | ||
| 1158 | count++; | ||
| 1159 | } | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | if (count != serializable) | ||
| 1163 | throw new IOException("wrote different number of service providers than expected"); | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /** | ||
| 1167 | * called from BeanContextSupport readObject before it deserializes the | ||
| 1168 | * children ... | ||
| 1169 | * | ||
| 1170 | * This class will deserialize any Serializable BeanContextServiceProviders | ||
| 1171 | * serialized earlier thus making them available to the children when they | ||
| 1172 | * deserialized. | ||
| 1173 | * | ||
| 1174 | * subclasses may envelope this method to insert their own serialization | ||
| 1175 | * processing that has to occur prior to serialization of the children | ||
| 1176 | */ | ||
| 1177 | |||
| 1178 | protected synchronized void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException { | ||
| 1179 | |||
| 1180 | serializable = ois.readInt(); | ||
| 1181 | |||
| 1182 | int count = serializable; | ||
| 1183 | |||
| 1184 | while (count > 0) { | ||
| 1185 | services.put(ois.readObject(), ois.readObject()); | ||
| 1186 | count--; | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | /** | ||
| 1191 | * serialize the instance | ||
| 1192 | */ | ||
| 1193 | |||
| 1194 | private synchronized void writeObject(ObjectOutputStream oos) throws IOException { | ||
| 1195 | oos.defaultWriteObject(); | ||
| 1196 | |||
| 1197 | serialize(oos, (Collection)bcsListeners); | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | /** | ||
| 1201 | * deserialize the instance | ||
| 1202 | */ | ||
| 1203 | |||
| 1204 | private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { | ||
| 1205 | |||
| 1206 | ois.defaultReadObject(); | ||
| 1207 | |||
| 1208 | deserialize(ois, (Collection)bcsListeners); | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | |||
| 1212 | /* | ||
| 1213 | * fields | ||
| 1214 | */ | ||
| 1215 | |||
| 1216 | /** | ||
| 1217 | * all accesses to the <code> protected transient HashMap services </code> | ||
| 1218 | * field should be synchronized on that object | ||
| 1219 | */ | ||
| 1220 | protected transient HashMap services; | ||
| 1221 | |||
| 1222 | /** | ||
| 1223 | * The number of instances of a serializable <tt>BeanContextServceProvider</tt>. | ||
| 1224 | */ | ||
| 1225 | protected transient int serializable = 0; | ||
| 1226 | |||
| 1227 | |||
| 1228 | /** | ||
| 1229 | * Delegate for the <tt>BeanContextServiceProvider</tt>. | ||
| 1230 | */ | ||
| 1231 | protected transient BCSSProxyServiceProvider proxy; | ||
| 1232 | |||
| 1233 | |||
| 1234 | /** | ||
| 1235 | * List of <tt>BeanContextServicesListener</tt> objects. | ||
| 1236 | */ | ||
| 1237 | protected transient ArrayList bcsListeners; | ||
| 1238 | } | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,1393 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package java.beans.beancontext; | ||
| 27 | |||
| 28 | import java.awt.Component; | ||
| 29 | import java.awt.Container; | ||
| 30 | |||
| 31 | import java.beans.Beans; | ||
| 32 | import java.beans.AppletInitializer; | ||
| 33 | |||
| 34 | import java.beans.DesignMode; | ||
| 35 | |||
| 36 | import java.beans.PropertyChangeEvent; | ||
| 37 | import java.beans.PropertyChangeListener; | ||
| 38 | import java.beans.PropertyChangeSupport; | ||
| 39 | |||
| 40 | import java.beans.VetoableChangeListener; | ||
| 41 | import java.beans.VetoableChangeSupport; | ||
| 42 | import java.beans.PropertyVetoException; | ||
| 43 | |||
| 44 | import java.beans.Visibility; | ||
| 45 | |||
| 46 | import java.io.IOException; | ||
| 47 | import java.io.InputStream; | ||
| 48 | import java.io.ObjectInputStream; | ||
| 49 | import java.io.ObjectOutputStream; | ||
| 50 | import java.io.Serializable; | ||
| 51 | |||
| 52 | import java.net.URL; | ||
| 53 | |||
| 54 | import java.util.ArrayList; | ||
| 55 | import java.util.Collection; | ||
| 56 | import java.util.HashMap; | ||
| 57 | import java.util.Iterator; | ||
| 58 | import java.util.Locale; | ||
| 59 | import java.util.Map; | ||
| 60 | |||
| 61 | |||
| 62 | /** | ||
| 63 | * This helper class provides a utility implementation of the | ||
| 64 | * java.beans.beancontext.BeanContext interface. | ||
| 65 | * </p> | ||
| 66 | * <p> | ||
| 67 | * Since this class directly implements the BeanContext interface, the class | ||
| 68 | * can, and is intended to be used either by subclassing this implementation, | ||
| 69 | * or via ad-hoc delegation of an instance of this class from another. | ||
| 70 | * </p> | ||
| 71 | * | ||
| 72 | * @author Laurence P. G. Cable | ||
| 73 | * @since 1.2 | ||
| 74 | */ | ||
| 75 | public class BeanContextSupport extends BeanContextChildSupport | ||
| 76 | implements BeanContext, | ||
| 77 | Serializable, | ||
| 78 | PropertyChangeListener, | ||
| 79 | VetoableChangeListener { | ||
| 80 | |||
| 81 | // Fix for bug 4282900 to pass JCK regression test | ||
| 82 | static final long serialVersionUID = -4879613978649577204L; | ||
| 83 | |||
| 84 | /** | ||
| 85 | * | ||
| 86 | * Construct a BeanContextSupport instance | ||
| 87 | * | ||
| 88 | * | ||
| 89 | * @param peer The peer <tt>BeanContext</tt> we are | ||
| 90 | * supplying an implementation for, | ||
| 91 | * or <tt>null</tt> | ||
| 92 | * if this object is its own peer | ||
| 93 | * @param lcle The current Locale for this BeanContext. If | ||
| 94 | * <tt>lcle</tt> is <tt>null</tt>, the default locale | ||
| 95 | * is assigned to the <tt>BeanContext</tt> instance. | ||
| 96 | * @param dTime The initial state, | ||
| 97 | * <tt>true</tt> if in design mode, | ||
| 98 | * <tt>false</tt> if runtime. | ||
| 99 | * @param visible The initial visibility. | ||
| 100 | * @see java.util.Locale#getDefault() | ||
| 101 | * @see java.util.Locale#setDefault(java.util.Locale) | ||
| 102 | */ | ||
| 103 | public BeanContextSupport(BeanContext peer, Locale lcle, boolean dTime, boolean visible) { | ||
| 104 | super(peer); | ||
| 105 | |||
| 106 | locale = lcle != null ? lcle : Locale.getDefault(); | ||
| 107 | designTime = dTime; | ||
| 108 | okToUseGui = visible; | ||
| 109 | |||
| 110 | initialize(); | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Create an instance using the specified Locale and design mode. | ||
| 115 | * | ||
| 116 | * @param peer The peer <tt>BeanContext</tt> we | ||
| 117 | * are supplying an implementation for, | ||
| 118 | * or <tt>null</tt> if this object is its own peer | ||
| 119 | * @param lcle The current Locale for this <tt>BeanContext</tt>. If | ||
| 120 | * <tt>lcle</tt> is <tt>null</tt>, the default locale | ||
| 121 | * is assigned to the <tt>BeanContext</tt> instance. | ||
| 122 | * @param dtime The initial state, <tt>true</tt> | ||
| 123 | * if in design mode, | ||
| 124 | * <tt>false</tt> if runtime. | ||
| 125 | * @see java.util.Locale#getDefault() | ||
| 126 | * @see java.util.Locale#setDefault(java.util.Locale) | ||
| 127 | */ | ||
| 128 | public BeanContextSupport(BeanContext peer, Locale lcle, boolean dtime) { | ||
| 129 | this (peer, lcle, dtime, true); | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Create an instance using the specified locale | ||
| 134 | * | ||
| 135 | * @param peer The peer BeanContext we are | ||
| 136 | * supplying an implementation for, | ||
| 137 | * or <tt>null</tt> if this object | ||
| 138 | * is its own peer | ||
| 139 | * @param lcle The current Locale for this | ||
| 140 | * <tt>BeanContext</tt>. If | ||
| 141 | * <tt>lcle</tt> is <tt>null</tt>, | ||
| 142 | * the default locale | ||
| 143 | * is assigned to the <tt>BeanContext</tt> | ||
| 144 | * instance. | ||
| 145 | * @see java.util.Locale#getDefault() | ||
| 146 | * @see java.util.Locale#setDefault(java.util.Locale) | ||
| 147 | */ | ||
| 148 | public BeanContextSupport(BeanContext peer, Locale lcle) { | ||
| 149 | this (peer, lcle, false, true); | ||
| 150 | } | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Create an instance using with a default locale | ||
| 154 | * | ||
| 155 | * @param peer The peer <tt>BeanContext</tt> we are | ||
| 156 | * supplying an implementation for, | ||
| 157 | * or <tt>null</tt> if this object | ||
| 158 | * is its own peer | ||
| 159 | */ | ||
| 160 | public BeanContextSupport(BeanContext peer) { | ||
| 161 | this (peer, null, false, true); | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * Create an instance that is not a delegate of another object | ||
| 166 | */ | ||
| 167 | |||
| 168 | public BeanContextSupport() { | ||
| 169 | this (null, null, false, true); | ||
| 170 | } | ||
| 171 | |||
| 172 | /** | ||
| 173 | * Gets the instance of <tt>BeanContext</tt> that | ||
| 174 | * this object is providing the implementation for. | ||
| 175 | * @return the BeanContext instance | ||
| 176 | */ | ||
| 177 | public BeanContext getBeanContextPeer() { return (BeanContext)getBeanContextChildPeer(); } | ||
| 178 | |||
| 179 | /** | ||
| 180 | * <p> | ||
| 181 | * The instantiateChild method is a convenience hook | ||
| 182 | * in BeanContext to simplify | ||
| 183 | * the task of instantiating a Bean, nested, | ||
| 184 | * into a <tt>BeanContext</tt>. | ||
| 185 | * </p> | ||
| 186 | * <p> | ||
| 187 | * The semantics of the beanName parameter are defined by java.beans.Beans.instantate. | ||
| 188 | * </p> | ||
| 189 | * | ||
| 190 | * @param beanName the name of the Bean to instantiate within this BeanContext | ||
| 191 | * @throws IOException if there is an I/O error when the bean is being deserialized | ||
| 192 | * @throws ClassNotFoundException if the class | ||
| 193 | * identified by the beanName parameter is not found | ||
| 194 | * @return the new object | ||
| 195 | */ | ||
| 196 | public Object instantiateChild(String beanName) | ||
| 197 | throws IOException, ClassNotFoundException { | ||
| 198 | BeanContext bc = getBeanContextPeer(); | ||
| 199 | |||
| 200 | return Beans.instantiate(bc.getClass().getClassLoader(), beanName, bc); | ||
| 201 | } | ||
| 202 | |||
| 203 | /** | ||
| 204 | * Gets the number of children currently nested in | ||
| 205 | * this BeanContext. | ||
| 206 | * | ||
| 207 | * @return number of children | ||
| 208 | */ | ||
| 209 | public int size() { | ||
| 210 | synchronized(children) { | ||
| 211 | return children.size(); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * Reports whether or not this | ||
| 217 | * <tt>BeanContext</tt> is empty. | ||
| 218 | * A <tt>BeanContext</tt> is considered | ||
| 219 | * empty when it contains zero | ||
| 220 | * nested children. | ||
| 221 | * @return if there are not children | ||
| 222 | */ | ||
| 223 | public boolean isEmpty() { | ||
| 224 | synchronized(children) { | ||
| 225 | return children.isEmpty(); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | /** | ||
| 230 | * Determines whether or not the specified object | ||
| 231 | * is currently a child of this <tt>BeanContext</tt>. | ||
| 232 | * @param o the Object in question | ||
| 233 | * @return if this object is a child | ||
| 234 | */ | ||
| 235 | public boolean contains(Object o) { | ||
| 236 | synchronized(children) { | ||
| 237 | return children.containsKey(o); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | /** | ||
| 242 | * Determines whether or not the specified object | ||
| 243 | * is currently a child of this <tt>BeanContext</tt>. | ||
| 244 | * @param o the Object in question | ||
| 245 | * @return if this object is a child | ||
| 246 | */ | ||
| 247 | public boolean containsKey(Object o) { | ||
| 248 | synchronized(children) { | ||
| 249 | return children.containsKey(o); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /** | ||
| 254 | * Gets all JavaBean or <tt>BeanContext</tt> instances | ||
| 255 | * currently nested in this <tt>BeanContext</tt>. | ||
| 256 | * @return an <tt>Iterator</tt> of the nested children | ||
| 257 | */ | ||
| 258 | public Iterator iterator() { | ||
| 259 | synchronized(children) { | ||
| 260 | return new BCSIterator(children.keySet().iterator()); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * Gets all JavaBean or <tt>BeanContext</tt> | ||
| 266 | * instances currently nested in this BeanContext. | ||
| 267 | */ | ||
| 268 | public Object[] toArray() { | ||
| 269 | synchronized(children) { | ||
| 270 | return children.keySet().toArray(); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | /** | ||
| 275 | * Gets an array containing all children of | ||
| 276 | * this <tt>BeanContext</tt> that match | ||
| 277 | * the types contained in arry. | ||
| 278 | * @param arry The array of object | ||
| 279 | * types that are of interest. | ||
| 280 | * @return an array of children | ||
| 281 | */ | ||
| 282 | public Object[] toArray(Object[] arry) { | ||
| 283 | synchronized(children) { | ||
| 284 | return children.keySet().toArray(arry); | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | |||
| 289 | /************************************************************************/ | ||
| 290 | |||
| 291 | /** | ||
| 292 | * protected final subclass that encapsulates an iterator but implements | ||
| 293 | * a noop remove() method. | ||
| 294 | */ | ||
| 295 | |||
| 296 | protected static final class BCSIterator implements Iterator { | ||
| 297 | BCSIterator(Iterator i) { super(); src = i; } | ||
| 298 | |||
| 299 | public boolean hasNext() { return src.hasNext(); } | ||
| 300 | public Object next() { return src.next(); } | ||
| 301 | public void remove() { /* do nothing */ } | ||
| 302 | |||
| 303 | private Iterator src; | ||
| 304 | } | ||
| 305 | |||
| 306 | /************************************************************************/ | ||
| 307 | |||
| 308 | /* | ||
| 309 | * protected nested class containing per child information, an instance | ||
| 310 | * of which is associated with each child in the "children" hashtable. | ||
| 311 | * subclasses can extend this class to include their own per-child state. | ||
| 312 | * | ||
| 313 | * Note that this 'value' is serialized with the corresponding child 'key' | ||
| 314 | * when the BeanContextSupport is serialized. | ||
| 315 | */ | ||
| 316 | |||
| 317 | protected class BCSChild implements Serializable { | ||
| 318 | |||
| 319 | private static final long serialVersionUID = -5815286101609939109L; | ||
| 320 | |||
| 321 | BCSChild(Object bcc, Object peer) { | ||
| 322 | super(); | ||
| 323 | |||
| 324 | child = bcc; | ||
| 325 | proxyPeer = peer; | ||
| 326 | } | ||
| 327 | |||
| 328 | Object getChild() { return child; } | ||
| 329 | |||
| 330 | void setRemovePending(boolean v) { removePending = v; } | ||
| 331 | |||
| 332 | boolean isRemovePending() { return removePending; } | ||
| 333 | |||
| 334 | boolean isProxyPeer() { return proxyPeer != null; } | ||
| 335 | |||
| 336 | Object getProxyPeer() { return proxyPeer; } | ||
| 337 | /* | ||
| 338 | * fields | ||
| 339 | */ | ||
| 340 | |||
| 341 | |||
| 342 | private Object child; | ||
| 343 | private Object proxyPeer; | ||
| 344 | |||
| 345 | private transient boolean removePending; | ||
| 346 | } | ||
| 347 | |||
| 348 | /** | ||
| 349 | * <p> | ||
| 350 | * Subclasses can override this method to insert their own subclass | ||
| 351 | * of Child without having to override add() or the other Collection | ||
| 352 | * methods that add children to the set. | ||
| 353 | * </p> | ||
| 354 | * | ||
| 355 | * @param targetChild the child to create the Child on behalf of | ||
| 356 | * @param peer the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy | ||
| 357 | */ | ||
| 358 | |||
| 359 | protected BCSChild createBCSChild(Object targetChild, Object peer) { | ||
| 360 | return new BCSChild(targetChild, peer); | ||
| 361 | } | ||
| 362 | |||
| 363 | /************************************************************************/ | ||
| 364 | |||
| 365 | /** | ||
| 366 | * Adds/nests a child within this <tt>BeanContext</tt>. | ||
| 367 | * <p> | ||
| 368 | * Invoked as a side effect of java.beans.Beans.instantiate(). | ||
| 369 | * If the child object is not valid for adding then this method | ||
| 370 | * throws an IllegalStateException. | ||
| 371 | * </p> | ||
| 372 | * | ||
| 373 | * | ||
| 374 | * @param targetChild The child objects to nest | ||
| 375 | * within this <tt>BeanContext</tt> | ||
| 376 | * @return true if the child was added successfully. | ||
| 377 | * @see #validatePendingAdd | ||
| 378 | */ | ||
| 379 | public boolean add(Object targetChild) { | ||
| 380 | |||
| 381 | if (targetChild == null) throw new IllegalArgumentException(); | ||
| 382 | |||
| 383 | // The specification requires that we do nothing if the child | ||
| 384 | // is already nested herein. | ||
| 385 | |||
| 386 | if (children.containsKey(targetChild)) return false; // test before locking | ||
| 387 | |||
| 388 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 389 | if (children.containsKey(targetChild)) return false; // check again | ||
| 390 | |||
| 391 | if (!validatePendingAdd(targetChild)) { | ||
| 392 | throw new IllegalStateException(); | ||
| 393 | } | ||
| 394 | |||
| 395 | |||
| 396 | // The specification requires that we invoke setBeanContext() on the | ||
| 397 | // newly added child if it implements the java.beans.beancontext.BeanContextChild interface | ||
| 398 | |||
| 399 | BeanContextChild cbcc = getChildBeanContextChild(targetChild); | ||
| 400 | BeanContextChild bccp = null; | ||
| 401 | |||
| 402 | synchronized(targetChild) { | ||
| 403 | |||
| 404 | if (targetChild instanceof BeanContextProxy) { | ||
| 405 | bccp = ((BeanContextProxy)targetChild).getBeanContextProxy(); | ||
| 406 | |||
| 407 | if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()"); | ||
| 408 | } | ||
| 409 | |||
| 410 | BCSChild bcsc = createBCSChild(targetChild, bccp); | ||
| 411 | BCSChild pbcsc = null; | ||
| 412 | |||
| 413 | synchronized (children) { | ||
| 414 | children.put(targetChild, bcsc); | ||
| 415 | |||
| 416 | if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild)); | ||
| 417 | } | ||
| 418 | |||
| 419 | if (cbcc != null) synchronized(cbcc) { | ||
| 420 | try { | ||
| 421 | cbcc.setBeanContext(getBeanContextPeer()); | ||
| 422 | } catch (PropertyVetoException pve) { | ||
| 423 | |||
| 424 | synchronized (children) { | ||
| 425 | children.remove(targetChild); | ||
| 426 | |||
| 427 | if (bccp != null) children.remove(bccp); | ||
| 428 | } | ||
| 429 | |||
| 430 | throw new IllegalStateException(); | ||
| 431 | } | ||
| 432 | |||
| 433 | cbcc.addPropertyChangeListener("beanContext", childPCL); | ||
| 434 | cbcc.addVetoableChangeListener("beanContext", childVCL); | ||
| 435 | } | ||
| 436 | |||
| 437 | Visibility v = getChildVisibility(targetChild); | ||
| 438 | |||
| 439 | if (v != null) { | ||
| 440 | if (okToUseGui) | ||
| 441 | v.okToUseGui(); | ||
| 442 | else | ||
| 443 | v.dontUseGui(); | ||
| 444 | } | ||
| 445 | |||
| 446 | if (getChildSerializable(targetChild) != null) serializable++; | ||
| 447 | |||
| 448 | childJustAddedHook(targetChild, bcsc); | ||
| 449 | |||
| 450 | if (bccp != null) { | ||
| 451 | v = getChildVisibility(bccp); | ||
| 452 | |||
| 453 | if (v != null) { | ||
| 454 | if (okToUseGui) | ||
| 455 | v.okToUseGui(); | ||
| 456 | else | ||
| 457 | v.dontUseGui(); | ||
| 458 | } | ||
| 459 | |||
| 460 | if (getChildSerializable(bccp) != null) serializable++; | ||
| 461 | |||
| 462 | childJustAddedHook(bccp, pbcsc); | ||
| 463 | } | ||
| 464 | |||
| 465 | |||
| 466 | } | ||
| 467 | |||
| 468 | // The specification requires that we fire a notification of the change | ||
| 469 | |||
| 470 | fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } )); | ||
| 471 | |||
| 472 | } | ||
| 473 | |||
| 474 | return true; | ||
| 475 | } | ||
| 476 | |||
| 477 | /** | ||
| 478 | * Removes a child from this BeanContext. If the child object is not | ||
| 479 | * for adding then this method throws an IllegalStateException. | ||
| 480 | * @param targetChild The child objects to remove | ||
| 481 | * @see #validatePendingRemove | ||
| 482 | */ | ||
| 483 | public boolean remove(Object targetChild) { | ||
| 484 | return remove(targetChild, true); | ||
| 485 | } | ||
| 486 | |||
| 487 | /** | ||
| 488 | * internal remove used when removal caused by | ||
| 489 | * unexpected <tt>setBeanContext</tt> or | ||
| 490 | * by <tt>remove()</tt> invocation. | ||
| 491 | * @param targetChild the JavaBean, BeanContext, or Object to be removed | ||
| 492 | * @param callChildSetBC used to indicate that | ||
| 493 | * the child should be notified that it is no | ||
| 494 | * longer nested in this <tt>BeanContext</tt>. | ||
| 495 | */ | ||
| 496 | protected boolean remove(Object targetChild, boolean callChildSetBC) { | ||
| 497 | |||
| 498 | if (targetChild == null) throw new IllegalArgumentException(); | ||
| 499 | |||
| 500 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 501 | if (!containsKey(targetChild)) return false; | ||
| 502 | |||
| 503 | if (!validatePendingRemove(targetChild)) { | ||
| 504 | throw new IllegalStateException(); | ||
| 505 | } | ||
| 506 | |||
| 507 | BCSChild bcsc = (BCSChild)children.get(targetChild); | ||
| 508 | BCSChild pbcsc = null; | ||
| 509 | Object peer = null; | ||
| 510 | |||
| 511 | // we are required to notify the child that it is no longer nested here if | ||
| 512 | // it implements java.beans.beancontext.BeanContextChild | ||
| 513 | |||
| 514 | synchronized(targetChild) { | ||
| 515 | if (callChildSetBC) { | ||
| 516 | BeanContextChild cbcc = getChildBeanContextChild(targetChild); | ||
| 517 | if (cbcc != null) synchronized(cbcc) { | ||
| 518 | cbcc.removePropertyChangeListener("beanContext", childPCL); | ||
| 519 | cbcc.removeVetoableChangeListener("beanContext", childVCL); | ||
| 520 | |||
| 521 | try { | ||
| 522 | cbcc.setBeanContext(null); | ||
| 523 | } catch (PropertyVetoException pve1) { | ||
| 524 | cbcc.addPropertyChangeListener("beanContext", childPCL); | ||
| 525 | cbcc.addVetoableChangeListener("beanContext", childVCL); | ||
| 526 | throw new IllegalStateException(); | ||
| 527 | } | ||
| 528 | |||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | synchronized (children) { | ||
| 533 | children.remove(targetChild); | ||
| 534 | |||
| 535 | if (bcsc.isProxyPeer()) { | ||
| 536 | pbcsc = (BCSChild)children.get(peer = bcsc.getProxyPeer()); | ||
| 537 | children.remove(peer); | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | if (getChildSerializable(targetChild) != null) serializable--; | ||
| 542 | |||
| 543 | childJustRemovedHook(targetChild, bcsc); | ||
| 544 | |||
| 545 | if (peer != null) { | ||
| 546 | if (getChildSerializable(peer) != null) serializable--; | ||
| 547 | |||
| 548 | childJustRemovedHook(peer, pbcsc); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } )); | ||
| 553 | |||
| 554 | } | ||
| 555 | |||
| 556 | return true; | ||
| 557 | } | ||
| 558 | |||
| 559 | /** | ||
| 560 | * Tests to see if all objects in the | ||
| 561 | * specified <tt>Collection</tt> are children of | ||
| 562 | * this <tt>BeanContext</tt>. | ||
| 563 | * @param c the specified <tt>Collection</tt> | ||
| 564 | * | ||
| 565 | * @return <tt>true</tt> if all objects | ||
| 566 | * in the collection are children of | ||
| 567 | * this <tt>BeanContext</tt>, false if not. | ||
| 568 | */ | ||
| 569 | public boolean containsAll(Collection c) { | ||
| 570 | synchronized(children) { | ||
| 571 | Iterator i = c.iterator(); | ||
| 572 | while (i.hasNext()) | ||
| 573 | if(!contains(i.next())) | ||
| 574 | return false; | ||
| 575 | |||
| 576 | return true; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | /** | ||
| 581 | * add Collection to set of Children (Unsupported) | ||
| 582 | * implementations must synchronized on the hierarchy lock and "children" protected field | ||
| 583 | * @throws UnsupportedOperationException | ||
| 584 | */ | ||
| 585 | public boolean addAll(Collection c) { | ||
| 586 | throw new UnsupportedOperationException(); | ||
| 587 | } | ||
| 588 | |||
| 589 | /** | ||
| 590 | * remove all specified children (Unsupported) | ||
| 591 | * implementations must synchronized on the hierarchy lock and "children" protected field | ||
| 592 | * @throws UnsupportedOperationException | ||
| 593 | */ | ||
| 594 | public boolean removeAll(Collection c) { | ||
| 595 | throw new UnsupportedOperationException(); | ||
| 596 | } | ||
| 597 | |||
| 598 | |||
| 599 | /** | ||
| 600 | * retain only specified children (Unsupported) | ||
| 601 | * implementations must synchronized on the hierarchy lock and "children" protected field | ||
| 602 | * @throws UnsupportedOperationException | ||
| 603 | */ | ||
| 604 | public boolean retainAll(Collection c) { | ||
| 605 | throw new UnsupportedOperationException(); | ||
| 606 | } | ||
| 607 | |||
| 608 | /** | ||
| 609 | * clear the children (Unsupported) | ||
| 610 | * implementations must synchronized on the hierarchy lock and "children" protected field | ||
| 611 | * @throws UnsupportedOperationException | ||
| 612 | */ | ||
| 613 | public void clear() { | ||
| 614 | throw new UnsupportedOperationException(); | ||
| 615 | } | ||
| 616 | |||
| 617 | /** | ||
| 618 | * Adds a BeanContextMembershipListener | ||
| 619 | * | ||
| 620 | * @param bcml the BeanContextMembershipListener to add | ||
| 621 | * @throws NullPointerException | ||
| 622 | */ | ||
| 623 | |||
| 624 | public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) { | ||
| 625 | if (bcml == null) throw new NullPointerException("listener"); | ||
| 626 | |||
| 627 | synchronized(bcmListeners) { | ||
| 628 | if (bcmListeners.contains(bcml)) | ||
| 629 | return; | ||
| 630 | else | ||
| 631 | bcmListeners.add(bcml); | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | /** | ||
| 636 | * Removes a BeanContextMembershipListener | ||
| 637 | * | ||
| 638 | * @param bcml the BeanContextMembershipListener to remove | ||
| 639 | * @throws NullPointerException | ||
| 640 | */ | ||
| 641 | |||
| 642 | public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) { | ||
| 643 | if (bcml == null) throw new NullPointerException("listener"); | ||
| 644 | |||
| 645 | synchronized(bcmListeners) { | ||
| 646 | if (!bcmListeners.contains(bcml)) | ||
| 647 | return; | ||
| 648 | else | ||
| 649 | bcmListeners.remove(bcml); | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | /** | ||
| 654 | * @param name the name of the resource requested. | ||
| 655 | * @param bcc the child object making the request. | ||
| 656 | * | ||
| 657 | * @return the requested resource as an InputStream | ||
| 658 | * @throws NullPointerException | ||
| 659 | */ | ||
| 660 | |||
| 661 | public InputStream getResourceAsStream(String name, BeanContextChild bcc) { | ||
| 662 | if (name == null) throw new NullPointerException("name"); | ||
| 663 | if (bcc == null) throw new NullPointerException("bcc"); | ||
| 664 | |||
| 665 | if (containsKey(bcc)) { | ||
| 666 | ClassLoader cl = bcc.getClass().getClassLoader(); | ||
| 667 | |||
| 668 | return cl != null ? cl.getResourceAsStream(name) | ||
| 669 | : ClassLoader.getSystemResourceAsStream(name); | ||
| 670 | } else throw new IllegalArgumentException("Not a valid child"); | ||
| 671 | } | ||
| 672 | |||
| 673 | /** | ||
| 674 | * @param name the name of the resource requested. | ||
| 675 | * @param bcc the child object making the request. | ||
| 676 | * | ||
| 677 | * @return the requested resource as an InputStream | ||
| 678 | */ | ||
| 679 | |||
| 680 | public URL getResource(String name, BeanContextChild bcc) { | ||
| 681 | if (name == null) throw new NullPointerException("name"); | ||
| 682 | if (bcc == null) throw new NullPointerException("bcc"); | ||
| 683 | |||
| 684 | if (containsKey(bcc)) { | ||
| 685 | ClassLoader cl = bcc.getClass().getClassLoader(); | ||
| 686 | |||
| 687 | return cl != null ? cl.getResource(name) | ||
| 688 | : ClassLoader.getSystemResource(name); | ||
| 689 | } else throw new IllegalArgumentException("Not a valid child"); | ||
| 690 | } | ||
| 691 | |||
| 692 | /** | ||
| 693 | * Sets the new design time value for this <tt>BeanContext</tt>. | ||
| 694 | * @param dTime the new designTime value | ||
| 695 | */ | ||
| 696 | public synchronized void setDesignTime(boolean dTime) { | ||
| 697 | if (designTime != dTime) { | ||
| 698 | designTime = dTime; | ||
| 699 | |||
| 700 | firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime)); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | |||
| 704 | |||
| 705 | /** | ||
| 706 | * Reports whether or not this object is in | ||
| 707 | * currently in design time mode. | ||
| 708 | * @return <tt>true</tt> if in design time mode, | ||
| 709 | * <tt>false</tt> if not | ||
| 710 | */ | ||
| 711 | public synchronized boolean isDesignTime() { return designTime; } | ||
| 712 | |||
| 713 | /** | ||
| 714 | * Sets the locale of this BeanContext. | ||
| 715 | * @param newLocale the new locale. This method call will have | ||
| 716 | * no effect if newLocale is <CODE>null</CODE>. | ||
| 717 | * @throws PropertyVetoException if the new value is rejected | ||
| 718 | */ | ||
| 719 | public synchronized void setLocale(Locale newLocale) throws PropertyVetoException { | ||
| 720 | |||
| 721 | if ((locale != null && !locale.equals(newLocale)) && newLocale != null) { | ||
| 722 | Locale old = locale; | ||
| 723 | |||
| 724 | fireVetoableChange("locale", old, newLocale); // throws | ||
| 725 | |||
| 726 | locale = newLocale; | ||
| 727 | |||
| 728 | firePropertyChange("locale", old, newLocale); | ||
| 729 | } | ||
| 730 | } | ||
| 731 | |||
| 732 | /** | ||
| 733 | * Gets the locale for this <tt>BeanContext</tt>. | ||
| 734 | * | ||
| 735 | * @return the current Locale of the <tt>BeanContext</tt> | ||
| 736 | */ | ||
| 737 | public synchronized Locale getLocale() { return locale; } | ||
| 738 | |||
| 739 | /** | ||
| 740 | * <p> | ||
| 741 | * This method is typically called from the environment in order to determine | ||
| 742 | * if the implementor "needs" a GUI. | ||
| 743 | * </p> | ||
| 744 | * <p> | ||
| 745 | * The algorithm used herein tests the BeanContextPeer, and its current children | ||
| 746 | * to determine if they are either Containers, Components, or if they implement | ||
| 747 | * Visibility and return needsGui() == true. | ||
| 748 | * </p> | ||
| 749 | * @return <tt>true</tt> if the implementor needs a GUI | ||
| 750 | */ | ||
| 751 | public synchronized boolean needsGui() { | ||
| 752 | BeanContext bc = getBeanContextPeer(); | ||
| 753 | |||
| 754 | if (bc != this) { | ||
| 755 | if (bc instanceof Visibility) return ((Visibility)bc).needsGui(); | ||
| 756 | |||
| 757 | if (bc instanceof Container || bc instanceof Component) | ||
| 758 | return true; | ||
| 759 | } | ||
| 760 | |||
| 761 | synchronized(children) { | ||
| 762 | for (Iterator i = children.keySet().iterator(); i.hasNext();) { | ||
| 763 | Object c = i.next(); | ||
| 764 | |||
| 765 | try { | ||
| 766 | return ((Visibility)c).needsGui(); | ||
| 767 | } catch (ClassCastException cce) { | ||
| 768 | // do nothing ... | ||
| 769 | } | ||
| 770 | |||
| 771 | if (c instanceof Container || c instanceof Component) | ||
| 772 | return true; | ||
| 773 | } | ||
| 774 | } | ||
| 775 | |||
| 776 | return false; | ||
| 777 | } | ||
| 778 | |||
| 779 | /** | ||
| 780 | * notify this instance that it may no longer render a GUI. | ||
| 781 | */ | ||
| 782 | |||
| 783 | public synchronized void dontUseGui() { | ||
| 784 | if (okToUseGui) { | ||
| 785 | okToUseGui = false; | ||
| 786 | |||
| 787 | // lets also tell the Children that can that they may not use their GUI's | ||
| 788 | synchronized(children) { | ||
| 789 | for (Iterator i = children.keySet().iterator(); i.hasNext();) { | ||
| 790 | Visibility v = getChildVisibility(i.next()); | ||
| 791 | |||
| 792 | if (v != null) v.dontUseGui(); | ||
| 793 | } | ||
| 794 | } | ||
| 795 | } | ||
| 796 | } | ||
| 797 | |||
| 798 | /** | ||
| 799 | * Notify this instance that it may now render a GUI | ||
| 800 | */ | ||
| 801 | |||
| 802 | public synchronized void okToUseGui() { | ||
| 803 | if (!okToUseGui) { | ||
| 804 | okToUseGui = true; | ||
| 805 | |||
| 806 | // lets also tell the Children that can that they may use their GUI's | ||
| 807 | synchronized(children) { | ||
| 808 | for (Iterator i = children.keySet().iterator(); i.hasNext();) { | ||
| 809 | Visibility v = getChildVisibility(i.next()); | ||
| 810 | |||
| 811 | if (v != null) v.okToUseGui(); | ||
| 812 | } | ||
| 813 | } | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | /** | ||
| 818 | * Used to determine if the <tt>BeanContext</tt> | ||
| 819 | * child is avoiding using its GUI. | ||
| 820 | * @return is this instance avoiding using its GUI? | ||
| 821 | * @see Visibility | ||
| 822 | */ | ||
| 823 | public boolean avoidingGui() { | ||
| 824 | return !okToUseGui && needsGui(); | ||
| 825 | } | ||
| 826 | |||
| 827 | /** | ||
| 828 | * Is this <tt>BeanContext</tt> in the | ||
| 829 | * process of being serialized? | ||
| 830 | * @return if this <tt>BeanContext</tt> is | ||
| 831 | * currently being serialized | ||
| 832 | */ | ||
| 833 | public boolean isSerializing() { return serializing; } | ||
| 834 | |||
| 835 | /** | ||
| 836 | * Returns an iterator of all children | ||
| 837 | * of this <tt>BeanContext</tt>. | ||
| 838 | * @return an iterator for all the current BCSChild values | ||
| 839 | */ | ||
| 840 | protected Iterator bcsChildren() { synchronized(children) { return children.values().iterator(); } } | ||
| 841 | |||
| 842 | /** | ||
| 843 | * called by writeObject after defaultWriteObject() but prior to | ||
| 844 | * serialization of currently serializable children. | ||
| 845 | * | ||
| 846 | * This method may be overridden by subclasses to perform custom | ||
| 847 | * serialization of their state prior to this superclass serializing | ||
| 848 | * the children. | ||
| 849 | * | ||
| 850 | * This method should not however be used by subclasses to replace their | ||
| 851 | * own implementation (if any) of writeObject(). | ||
| 852 | */ | ||
| 853 | |||
| 854 | protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException { | ||
| 855 | } | ||
| 856 | |||
| 857 | /** | ||
| 858 | * called by readObject after defaultReadObject() but prior to | ||
| 859 | * deserialization of any children. | ||
| 860 | * | ||
| 861 | * This method may be overridden by subclasses to perform custom | ||
| 862 | * deserialization of their state prior to this superclass deserializing | ||
| 863 | * the children. | ||
| 864 | * | ||
| 865 | * This method should not however be used by subclasses to replace their | ||
| 866 | * own implementation (if any) of readObject(). | ||
| 867 | */ | ||
| 868 | |||
| 869 | protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException { | ||
| 870 | } | ||
| 871 | |||
| 872 | /** | ||
| 873 | * Called by readObject with the newly deserialized child and BCSChild. | ||
| 874 | * @param child the newly deserialized child | ||
| 875 | * @param bcsc the newly deserialized BCSChild | ||
| 876 | */ | ||
| 877 | protected void childDeserializedHook(Object child, BCSChild bcsc) { | ||
| 878 | synchronized(children) { | ||
| 879 | children.put(child, bcsc); | ||
| 880 | } | ||
| 881 | } | ||
| 882 | |||
| 883 | /** | ||
| 884 | * Used by writeObject to serialize a Collection. | ||
| 885 | * @param oos the <tt>ObjectOutputStream</tt> | ||
| 886 | * to use during serialization | ||
| 887 | * @param coll the <tt>Collection</tt> to serialize | ||
| 888 | * @throws IOException if serialization failed | ||
| 889 | */ | ||
| 890 | protected final void serialize(ObjectOutputStream oos, Collection coll) throws IOException { | ||
| 891 | int count = 0; | ||
| 892 | Object[] objects = coll.toArray(); | ||
| 893 | |||
| 894 | for (int i = 0; i < objects.length; i++) { | ||
| 895 | if (objects[i] instanceof Serializable) | ||
| 896 | count++; | ||
| 897 | else | ||
| 898 | objects[i] = null; | ||
| 899 | } | ||
| 900 | |||
| 901 | oos.writeInt(count); // number of subsequent objects | ||
| 902 | |||
| 903 | for (int i = 0; count > 0; i++) { | ||
| 904 | Object o = objects[i]; | ||
| 905 | |||
| 906 | if (o != null) { | ||
| 907 | oos.writeObject(o); | ||
| 908 | count--; | ||
| 909 | } | ||
| 910 | } | ||
| 911 | } | ||
| 912 | |||
| 913 | /** | ||
| 914 | * used by readObject to deserialize a collection. | ||
| 915 | * @param ois the ObjectInputStream to use | ||
| 916 | * @param coll the Collection | ||
| 917 | */ | ||
| 918 | protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException { | ||
| 919 | int count = 0; | ||
| 920 | |||
| 921 | count = ois.readInt(); | ||
| 922 | |||
| 923 | while (count-- > 0) { | ||
| 924 | coll.add(ois.readObject()); | ||
| 925 | } | ||
| 926 | } | ||
| 927 | |||
| 928 | /** | ||
| 929 | * Used to serialize all children of | ||
| 930 | * this <tt>BeanContext</tt>. | ||
| 931 | * @param oos the <tt>ObjectOutputStream</tt> | ||
| 932 | * to use during serialization | ||
| 933 | * @throws IOException if serialization failed | ||
| 934 | */ | ||
| 935 | public final void writeChildren(ObjectOutputStream oos) throws IOException { | ||
| 936 | if (serializable <= 0) return; | ||
| 937 | |||
| 938 | boolean prev = serializing; | ||
| 939 | |||
| 940 | serializing = true; | ||
| 941 | |||
| 942 | int count = 0; | ||
| 943 | |||
| 944 | synchronized(children) { | ||
| 945 | Iterator i = children.entrySet().iterator(); | ||
| 946 | |||
| 947 | while (i.hasNext() && count < serializable) { | ||
| 948 | Map.Entry entry = (Map.Entry)i.next(); | ||
| 949 | |||
| 950 | if (entry.getKey() instanceof Serializable) { | ||
| 951 | try { | ||
| 952 | oos.writeObject(entry.getKey()); // child | ||
| 953 | oos.writeObject(entry.getValue()); // BCSChild | ||
| 954 | } catch (IOException ioe) { | ||
| 955 | serializing = prev; | ||
| 956 | throw ioe; | ||
| 957 | } | ||
| 958 | count++; | ||
| 959 | } | ||
| 960 | } | ||
| 961 | } | ||
| 962 | |||
| 963 | serializing = prev; | ||
| 964 | |||
| 965 | if (count != serializable) { | ||
| 966 | throw new IOException("wrote different number of children than expected"); | ||
| 967 | } | ||
| 968 | |||
| 969 | } | ||
| 970 | |||
| 971 | /** | ||
| 972 | * Serialize the BeanContextSupport, if this instance has a distinct | ||
| 973 | * peer (that is this object is acting as a delegate for another) then | ||
| 974 | * the children of this instance are not serialized here due to a | ||
| 975 | * 'chicken and egg' problem that occurs on deserialization of the | ||
| 976 | * children at the same time as this instance. | ||
| 977 | * | ||
| 978 | * Therefore in situations where there is a distinct peer to this instance | ||
| 979 | * it should always call writeObject() followed by writeChildren() and | ||
| 980 | * readObject() followed by readChildren(). | ||
| 981 | * | ||
| 982 | * @param oos the ObjectOutputStream | ||
| 983 | */ | ||
| 984 | |||
| 985 | private synchronized void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException { | ||
| 986 | serializing = true; | ||
| 987 | |||
| 988 | synchronized (BeanContext.globalHierarchyLock) { | ||
| 989 | try { | ||
| 990 | oos.defaultWriteObject(); // serialize the BeanContextSupport object | ||
| 991 | |||
| 992 | bcsPreSerializationHook(oos); | ||
| 993 | |||
| 994 | if (serializable > 0 && this.equals(getBeanContextPeer())) | ||
| 995 | writeChildren(oos); | ||
| 996 | |||
| 997 | serialize(oos, (Collection)bcmListeners); | ||
| 998 | } finally { | ||
| 999 | serializing = false; | ||
| 1000 | } | ||
| 1001 | } | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | /** | ||
| 1005 | * When an instance of this class is used as a delegate for the | ||
| 1006 | * implementation of the BeanContext protocols (and its subprotocols) | ||
| 1007 | * there exists a 'chicken and egg' problem during deserialization | ||
| 1008 | */ | ||
| 1009 | |||
| 1010 | public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException { | ||
| 1011 | int count = serializable; | ||
| 1012 | |||
| 1013 | while (count-- > 0) { | ||
| 1014 | Object child = null; | ||
| 1015 | BeanContextSupport.BCSChild bscc = null; | ||
| 1016 | |||
| 1017 | try { | ||
| 1018 | child = ois.readObject(); | ||
| 1019 | bscc = (BeanContextSupport.BCSChild)ois.readObject(); | ||
| 1020 | } catch (IOException ioe) { | ||
| 1021 | continue; | ||
| 1022 | } catch (ClassNotFoundException cnfe) { | ||
| 1023 | continue; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | |||
| 1027 | synchronized(child) { | ||
| 1028 | BeanContextChild bcc = null; | ||
| 1029 | |||
| 1030 | try { | ||
| 1031 | bcc = (BeanContextChild)child; | ||
| 1032 | } catch (ClassCastException cce) { | ||
| 1033 | // do nothing; | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | if (bcc != null) { | ||
| 1037 | try { | ||
| 1038 | bcc.setBeanContext(getBeanContextPeer()); | ||
| 1039 | |||
| 1040 | bcc.addPropertyChangeListener("beanContext", childPCL); | ||
| 1041 | bcc.addVetoableChangeListener("beanContext", childVCL); | ||
| 1042 | |||
| 1043 | } catch (PropertyVetoException pve) { | ||
| 1044 | continue; | ||
| 1045 | } | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | childDeserializedHook(child, bscc); | ||
| 1049 | } | ||
| 1050 | } | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | /** | ||
| 1054 | * deserialize contents ... if this instance has a distinct peer the | ||
| 1055 | * children are *not* serialized here, the peer's readObject() must call | ||
| 1056 | * readChildren() after deserializing this instance. | ||
| 1057 | */ | ||
| 1058 | |||
| 1059 | private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { | ||
| 1060 | |||
| 1061 | synchronized(BeanContext.globalHierarchyLock) { | ||
| 1062 | ois.defaultReadObject(); | ||
| 1063 | |||
| 1064 | initialize(); | ||
| 1065 | |||
| 1066 | bcsPreDeserializationHook(ois); | ||
| 1067 | |||
| 1068 | if (serializable > 0 && this.equals(getBeanContextPeer())) | ||
| 1069 | readChildren(ois); | ||
| 1070 | |||
| 1071 | deserialize(ois, bcmListeners = new ArrayList(1)); | ||
| 1072 | } | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | /** | ||
| 1076 | * subclasses may envelope to monitor veto child property changes. | ||
| 1077 | */ | ||
| 1078 | |||
| 1079 | public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { | ||
| 1080 | String propertyName = pce.getPropertyName(); | ||
| 1081 | Object source = pce.getSource(); | ||
| 1082 | |||
| 1083 | synchronized(children) { | ||
| 1084 | if ("beanContext".equals(propertyName) && | ||
| 1085 | containsKey(source) && | ||
| 1086 | !getBeanContextPeer().equals(pce.getNewValue()) | ||
| 1087 | ) { | ||
| 1088 | if (!validatePendingRemove(source)) { | ||
| 1089 | throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce); | ||
| 1090 | } else ((BCSChild)children.get(source)).setRemovePending(true); | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | /** | ||
| 1096 | * subclasses may envelope to monitor child property changes. | ||
| 1097 | */ | ||
| 1098 | |||
| 1099 | public void propertyChange(PropertyChangeEvent pce) { | ||
| 1100 | String propertyName = pce.getPropertyName(); | ||
| 1101 | Object source = pce.getSource(); | ||
| 1102 | |||
| 1103 | synchronized(children) { | ||
| 1104 | if ("beanContext".equals(propertyName) && | ||
| 1105 | containsKey(source) && | ||
| 1106 | ((BCSChild)children.get(source)).isRemovePending()) { | ||
| 1107 | BeanContext bc = getBeanContextPeer(); | ||
| 1108 | |||
| 1109 | if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) { | ||
| 1110 | remove(source, false); | ||
| 1111 | } else { | ||
| 1112 | ((BCSChild)children.get(source)).setRemovePending(false); | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | /** | ||
| 1119 | * <p> | ||
| 1120 | * Subclasses of this class may override, or envelope, this method to | ||
| 1121 | * add validation behavior for the BeanContext to examine child objects | ||
| 1122 | * immediately prior to their being added to the BeanContext. | ||
| 1123 | * </p> | ||
| 1124 | * | ||
| 1125 | * @return true iff the child may be added to this BeanContext, otherwise false. | ||
| 1126 | */ | ||
| 1127 | |||
| 1128 | protected boolean validatePendingAdd(Object targetChild) { | ||
| 1129 | return true; | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | /** | ||
| 1133 | * <p> | ||
| 1134 | * Subclasses of this class may override, or envelope, this method to | ||
| 1135 | * add validation behavior for the BeanContext to examine child objects | ||
| 1136 | * immediately prior to their being removed from the BeanContext. | ||
| 1137 | * </p> | ||
| 1138 | * | ||
| 1139 | * @return true iff the child may be removed from this BeanContext, otherwise false. | ||
| 1140 | */ | ||
| 1141 | |||
| 1142 | protected boolean validatePendingRemove(Object targetChild) { | ||
| 1143 | return true; | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | /** | ||
| 1147 | * subclasses may override this method to simply extend add() semantics | ||
| 1148 | * after the child has been added and before the event notification has | ||
| 1149 | * occurred. The method is called with the child synchronized. | ||
| 1150 | */ | ||
| 1151 | |||
| 1152 | protected void childJustAddedHook(Object child, BCSChild bcsc) { | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | /** | ||
| 1156 | * subclasses may override this method to simply extend remove() semantics | ||
| 1157 | * after the child has been removed and before the event notification has | ||
| 1158 | * occurred. The method is called with the child synchronized. | ||
| 1159 | */ | ||
| 1160 | |||
| 1161 | protected void childJustRemovedHook(Object child, BCSChild bcsc) { | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | /** | ||
| 1165 | * Gets the Component (if any) associated with the specified child. | ||
| 1166 | * @param child the specified child | ||
| 1167 | * @return the Component (if any) associated with the specified child. | ||
| 1168 | */ | ||
| 1169 | protected static final Visibility getChildVisibility(Object child) { | ||
| 1170 | try { | ||
| 1171 | return (Visibility)child; | ||
| 1172 | } catch (ClassCastException cce) { | ||
| 1173 | return null; | ||
| 1174 | } | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | /** | ||
| 1178 | * Gets the Serializable (if any) associated with the specified Child | ||
| 1179 | * @param child the specified child | ||
| 1180 | * @return the Serializable (if any) associated with the specified Child | ||
| 1181 | */ | ||
| 1182 | protected static final Serializable getChildSerializable(Object child) { | ||
| 1183 | try { | ||
| 1184 | return (Serializable)child; | ||
| 1185 | } catch (ClassCastException cce) { | ||
| 1186 | return null; | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | /** | ||
| 1191 | * Gets the PropertyChangeListener | ||
| 1192 | * (if any) of the specified child | ||
| 1193 | * @param child the specified child | ||
| 1194 | * @return the PropertyChangeListener (if any) of the specified child | ||
| 1195 | */ | ||
| 1196 | protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) { | ||
| 1197 | try { | ||
| 1198 | return (PropertyChangeListener)child; | ||
| 1199 | } catch (ClassCastException cce) { | ||
| 1200 | return null; | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | /** | ||
| 1205 | * Gets the VetoableChangeListener | ||
| 1206 | * (if any) of the specified child | ||
| 1207 | * @param child the specified child | ||
| 1208 | * @return the VetoableChangeListener (if any) of the specified child | ||
| 1209 | */ | ||
| 1210 | protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) { | ||
| 1211 | try { | ||
| 1212 | return (VetoableChangeListener)child; | ||
| 1213 | } catch (ClassCastException cce) { | ||
| 1214 | return null; | ||
| 1215 | } | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | /** | ||
| 1219 | * Gets the BeanContextMembershipListener | ||
| 1220 | * (if any) of the specified child | ||
| 1221 | * @param child the specified child | ||
| 1222 | * @return the BeanContextMembershipListener (if any) of the specified child | ||
| 1223 | */ | ||
| 1224 | protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) { | ||
| 1225 | try { | ||
| 1226 | return (BeanContextMembershipListener)child; | ||
| 1227 | } catch (ClassCastException cce) { | ||
| 1228 | return null; | ||
| 1229 | } | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | /** | ||
| 1233 | * Gets the BeanContextChild (if any) of the specified child | ||
| 1234 | * @param child the specified child | ||
| 1235 | * @return the BeanContextChild (if any) of the specified child | ||
| 1236 | * @throws IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy | ||
| 1237 | */ | ||
| 1238 | protected static final BeanContextChild getChildBeanContextChild(Object child) { | ||
| 1239 | try { | ||
| 1240 | BeanContextChild bcc = (BeanContextChild)child; | ||
| 1241 | |||
| 1242 | if (child instanceof BeanContextChild && child instanceof BeanContextProxy) | ||
| 1243 | throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy"); | ||
| 1244 | else | ||
| 1245 | return bcc; | ||
| 1246 | } catch (ClassCastException cce) { | ||
| 1247 | try { | ||
| 1248 | return ((BeanContextProxy)child).getBeanContextProxy(); | ||
| 1249 | } catch (ClassCastException cce1) { | ||
| 1250 | return null; | ||
| 1251 | } | ||
| 1252 | } | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | /** | ||
| 1256 | * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface | ||
| 1257 | */ | ||
| 1258 | |||
| 1259 | protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) { | ||
| 1260 | Object[] copy; | ||
| 1261 | |||
| 1262 | synchronized(bcmListeners) { copy = bcmListeners.toArray(); } | ||
| 1263 | |||
| 1264 | for (int i = 0; i < copy.length; i++) | ||
| 1265 | ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | /** | ||
| 1269 | * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface | ||
| 1270 | */ | ||
| 1271 | |||
| 1272 | protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) { | ||
| 1273 | Object[] copy; | ||
| 1274 | |||
| 1275 | synchronized(bcmListeners) { copy = bcmListeners.toArray(); } | ||
| 1276 | |||
| 1277 | for (int i = 0; i < copy.length; i++) | ||
| 1278 | ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme); | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | /** | ||
| 1282 | * protected method called from constructor and readObject to initialize | ||
| 1283 | * transient state of BeanContextSupport instance. | ||
| 1284 | * | ||
| 1285 | * This class uses this method to instantiate inner class listeners used | ||
| 1286 | * to monitor PropertyChange and VetoableChange events on children. | ||
| 1287 | * | ||
| 1288 | * subclasses may envelope this method to add their own initialization | ||
| 1289 | * behavior | ||
| 1290 | */ | ||
| 1291 | |||
| 1292 | protected synchronized void initialize() { | ||
| 1293 | children = new HashMap(serializable + 1); | ||
| 1294 | bcmListeners = new ArrayList(1); | ||
| 1295 | |||
| 1296 | childPCL = new PropertyChangeListener() { | ||
| 1297 | |||
| 1298 | /* | ||
| 1299 | * this adaptor is used by the BeanContextSupport class to forward | ||
| 1300 | * property changes from a child to the BeanContext, avoiding | ||
| 1301 | * accidential serialization of the BeanContext by a badly | ||
| 1302 | * behaved Serializable child. | ||
| 1303 | */ | ||
| 1304 | |||
| 1305 | public void propertyChange(PropertyChangeEvent pce) { | ||
| 1306 | BeanContextSupport.this.propertyChange(pce); | ||
| 1307 | } | ||
| 1308 | }; | ||
| 1309 | |||
| 1310 | childVCL = new VetoableChangeListener() { | ||
| 1311 | |||
| 1312 | /* | ||
| 1313 | * this adaptor is used by the BeanContextSupport class to forward | ||
| 1314 | * vetoable changes from a child to the BeanContext, avoiding | ||
| 1315 | * accidential serialization of the BeanContext by a badly | ||
| 1316 | * behaved Serializable child. | ||
| 1317 | */ | ||
| 1318 | |||
| 1319 | public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { | ||
| 1320 | BeanContextSupport.this.vetoableChange(pce); | ||
| 1321 | } | ||
| 1322 | }; | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | /** | ||
| 1326 | * Gets a copy of the this BeanContext's children. | ||
| 1327 | * @return a copy of the current nested children | ||
| 1328 | */ | ||
| 1329 | protected final Object[] copyChildren() { | ||
| 1330 | synchronized(children) { return children.keySet().toArray(); } | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | /** | ||
| 1334 | * Tests to see if two class objects, | ||
| 1335 | * or their names are equal. | ||
| 1336 | * @param first the first object | ||
| 1337 | * @param second the second object | ||
| 1338 | * @return true if equal, false if not | ||
| 1339 | */ | ||
| 1340 | protected static final boolean classEquals(Class first, Class second) { | ||
| 1341 | return first.equals(second) || first.getName().equals(second.getName()); | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | |||
| 1345 | /* | ||
| 1346 | * fields | ||
| 1347 | */ | ||
| 1348 | |||
| 1349 | |||
| 1350 | /** | ||
| 1351 | * all accesses to the <code> protected HashMap children </code> field | ||
| 1352 | * shall be synchronized on that object. | ||
| 1353 | */ | ||
| 1354 | protected transient HashMap children; | ||
| 1355 | |||
| 1356 | private int serializable = 0; // children serializable | ||
| 1357 | |||
| 1358 | /** | ||
| 1359 | * all accesses to the <code> protected ArrayList bcmListeners </code> field | ||
| 1360 | * shall be synchronized on that object. | ||
| 1361 | */ | ||
| 1362 | protected transient ArrayList bcmListeners; | ||
| 1363 | |||
| 1364 | // | ||
| 1365 | |||
| 1366 | /** | ||
| 1367 | * The current locale of this BeanContext. | ||
| 1368 | */ | ||
| 1369 | protected Locale locale; | ||
| 1370 | |||
| 1371 | /** | ||
| 1372 | * A <tt>boolean</tt> indicating if this | ||
| 1373 | * instance may now render a GUI. | ||
| 1374 | */ | ||
| 1375 | protected boolean okToUseGui; | ||
| 1376 | |||
| 1377 | |||
| 1378 | /** | ||
| 1379 | * A <tt>boolean</tt> indicating whether or not | ||
| 1380 | * this object is currently in design time mode. | ||
| 1381 | */ | ||
| 1382 | protected boolean designTime; | ||
| 1383 | |||
| 1384 | /* | ||
| 1385 | * transient | ||
| 1386 | */ | ||
| 1387 | |||
| 1388 | private transient PropertyChangeListener childPCL; | ||
| 1389 | |||
| 1390 | private transient VetoableChangeListener childVCL; | ||
| 1391 | |||
| 1392 | private transient boolean serializing; | ||
| 1393 | } | ||
| @@ -0,0 +1,25 @@ | |||
| 1 | <../../../../../mkconfig | ||
| 2 | |||
| 3 | TARG=\ | ||
| 4 | BeanContext.dis\ | ||
| 5 | BeanContextChild.dis\ | ||
| 6 | BeanContextChildComponentProxy.dis\ | ||
| 7 | BeanContextChildSupport.dis\ | ||
| 8 | BeanContextContainerProxy.dis\ | ||
| 9 | BeanContextEvent.dis\ | ||
| 10 | BeanContextMembershipEvent.dis\ | ||
| 11 | BeanContextMembershipListener.dis\ | ||
| 12 | BeanContextProxy.dis\ | ||
| 13 | BeanContextServiceAvailableEvent.dis\ | ||
| 14 | BeanContextServiceProvider.dis\ | ||
| 15 | BeanContextServiceProviderBeanInfo.dis\ | ||
| 16 | BeanContextServiceRevokedEvent.dis\ | ||
| 17 | BeanContextServiceRevokedListener.dis\ | ||
| 18 | BeanContextServices.dis\ | ||
| 19 | BeanContextServicesListener.dis\ | ||
| 20 | BeanContextServicesSupport.dis\ | ||
| 21 | BeanContextSupport.dis\ | ||
| 22 | |||
| 23 | DISBIN=$ROOT/java/dis/java/java/java/beans/beancontext | ||
| 24 | |||
| 25 | <$ROOT/mkfiles/mkjava | ||
| @@ -0,0 +1,55 @@ | |||
| 1 | <!-- | ||
| 2 | Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | |||
| 5 | This code is free software; you can redistribute it and/or modify it | ||
| 6 | under the terms of the GNU General Public License version 2 only, as | ||
| 7 | published by the Free Software Foundation. Oracle designates this | ||
| 8 | particular file as subject to the "Classpath" exception as provided | ||
| 9 | by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | |||
| 11 | This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | accompanied this code). | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License version | ||
| 18 | 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | |||
| 21 | Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | or visit www.oracle.com if you need additional information or have any | ||
| 23 | questions. | ||
| 24 | --> | ||
| 25 | |||
| 26 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> | ||
| 27 | <html> | ||
| 28 | <body bgcolor="white"> | ||
| 29 | |||
| 30 | Provides classes and interfaces relating to bean context. | ||
| 31 | A bean context is a container for beans and defines the execution | ||
| 32 | environment for the beans it contains. There can be several beans in | ||
| 33 | a single bean context, and a bean context can be nested within another | ||
| 34 | bean context. This package also contains events and listener | ||
| 35 | interface for beans being added and removed from a bean context. | ||
| 36 | |||
| 37 | <!-- | ||
| 38 | <h2>Package Specification</h2> | ||
| 39 | |||
| 40 | ##### FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT ##### | ||
| 41 | <ul> | ||
| 42 | <li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a> | ||
| 43 | </ul> | ||
| 44 | |||
| 45 | <h2>Related Documentation</h2> | ||
| 46 | |||
| 47 | For overviews, tutorials, examples, guides, and tool documentation, please see: | ||
| 48 | <ul> | ||
| 49 | <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a> | ||
| 50 | </ul> | ||
| 51 | --> | ||
| 52 | |||
| 53 | @since 1.2 | ||
| 54 | </body> | ||
| 55 | </html> | ||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,54 @@ | |||
| 1 | <../../../../mkconfig | ||
| 2 | |||
| 3 | DIRS=\ | ||
| 4 | beancontext\ | ||
| 5 | |||
| 6 | TARG=\ | ||
| 7 | AppletInitializer.dis\ | ||
| 8 | BeanDescriptor.dis\ | ||
| 9 | BeanInfo.dis\ | ||
| 10 | Beans.dis\ | ||
| 11 | ChangeListenerMap.dis\ | ||
| 12 | ConstructorProperties.dis\ | ||
| 13 | Customizer.dis\ | ||
| 14 | DefaultPersistenceDelegate.dis\ | ||
| 15 | DesignMode.dis\ | ||
| 16 | Encoder.dis\ | ||
| 17 | EventHandler.dis\ | ||
| 18 | EventSetDescriptor.dis\ | ||
| 19 | ExceptionListener.dis\ | ||
| 20 | Expression.dis\ | ||
| 21 | FeatureDescriptor.dis\ | ||
| 22 | IndexedPropertyChangeEvent.dis\ | ||
| 23 | IndexedPropertyDescriptor.dis\ | ||
| 24 | IntrospectionException.dis\ | ||
| 25 | Introspector.dis\ | ||
| 26 | MetaData.dis\ | ||
| 27 | MethodDescriptor.dis\ | ||
| 28 | NameGenerator.dis\ | ||
| 29 | ParameterDescriptor.dis\ | ||
| 30 | PersistenceDelegate.dis\ | ||
| 31 | PropertyChangeEvent.dis\ | ||
| 32 | PropertyChangeListener.dis\ | ||
| 33 | PropertyChangeListenerProxy.dis\ | ||
| 34 | PropertyChangeSupport.dis\ | ||
| 35 | PropertyDescriptor.dis\ | ||
| 36 | PropertyEditor.dis\ | ||
| 37 | PropertyEditorManager.dis\ | ||
| 38 | PropertyEditorSupport.dis\ | ||
| 39 | PropertyVetoException.dis\ | ||
| 40 | ReflectionUtils.dis\ | ||
| 41 | SimpleBeanInfo.dis\ | ||
| 42 | Statement.dis\ | ||
| 43 | Transient.dis\ | ||
| 44 | VetoableChangeListener.dis\ | ||
| 45 | VetoableChangeListenerProxy.dis\ | ||
| 46 | VetoableChangeSupport.dis\ | ||
| 47 | Visibility.dis\ | ||
| 48 | XMLDecoder.dis\ | ||
| 49 | XMLEncoder.dis\ | ||
| 50 | |||
| 51 | DISBIN=$ROOT/java/dis/java/java/java/beans | ||
| 52 | |||
| 53 | <$ROOT/mkfiles/mkjava | ||
| 54 | <$ROOT/mkfiles/mksubdirs | ||
| @@ -0,0 +1,156 @@ | |||
| 1 | <!-- | ||
| 2 | Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | |||
| 5 | This code is free software; you can redistribute it and/or modify it | ||
| 6 | under the terms of the GNU General Public License version 2 only, as | ||
| 7 | published by the Free Software Foundation. Oracle designates this | ||
| 8 | particular file as subject to the "Classpath" exception as provided | ||
| 9 | by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | |||
| 11 | This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | accompanied this code). | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License version | ||
| 18 | 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | |||
| 21 | Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | or visit www.oracle.com if you need additional information or have any | ||
| 23 | questions. | ||
| 24 | --> | ||
| 25 | |||
| 26 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> | ||
| 27 | <html> | ||
| 28 | <body bgcolor="white"> | ||
| 29 | |||
| 30 | Contains classes related to developing | ||
| 31 | <em>beans</em> -- components | ||
| 32 | based on the JavaBeans™ architecture. | ||
| 33 | A few of the | ||
| 34 | classes are used by beans while they run in an application. | ||
| 35 | For example, the event classes are | ||
| 36 | used by beans that fire property and vetoable change | ||
| 37 | events (see | ||
| 38 | {@link java.beans.PropertyChangeEvent}). However, most of the classes in this | ||
| 39 | package are meant to be used by a bean editor (that is, a development environment | ||
| 40 | for customizing and putting together beans to create an application). In | ||
| 41 | particular, these classes help the bean editor create a user | ||
| 42 | interface that the user can use to customize the bean. For example, a bean may | ||
| 43 | contain a property of a special type that a bean editor may not know how to handle. | ||
| 44 | By using the <code>PropertyEditor</code> interface, a bean developer can | ||
| 45 | provide an editor for this special type. | ||
| 46 | |||
| 47 | <p> | ||
| 48 | To minimize the resources used by a bean, the classes used by bean editors are loaded only | ||
| 49 | when the bean is being edited. They are not needed while the bean is running in an application | ||
| 50 | and therefore not loaded. This information is kept in what's called a bean-info (see {@link java.beans.BeanInfo}). | ||
| 51 | |||
| 52 | <p> | ||
| 53 | Unless explicitly stated, null values or empty Strings are not valid | ||
| 54 | parameters for the methods in this package. You may expect to see | ||
| 55 | exceptions if these parameters are used. | ||
| 56 | |||
| 57 | |||
| 58 | <h2>Long-Term Persistence</h2> | ||
| 59 | |||
| 60 | As of v1.4, | ||
| 61 | the <code>java.beans</code> package provides support for | ||
| 62 | <em>long-term persistence</em> -- reading and | ||
| 63 | writing a bean as a textual representation of its property values. | ||
| 64 | The property values are treated as beans, | ||
| 65 | and are recursively read or written to capture | ||
| 66 | their publicly available state. | ||
| 67 | This approach is suitable for long-term storage | ||
| 68 | because it relies only on public API, | ||
| 69 | rather than the likely-to-change private implementation. | ||
| 70 | |||
| 71 | <blockquote> | ||
| 72 | <hr> | ||
| 73 | <b>Note:</b> | ||
| 74 | The persistence scheme cannot automatically instantiate | ||
| 75 | custom inner classes, such as you might use for event handlers. | ||
| 76 | By using the {@link java.beans.EventHandler} class | ||
| 77 | instead of inner classes for custom event handlers, | ||
| 78 | you can avoid this problem. | ||
| 79 | <hr> | ||
| 80 | </blockquote> | ||
| 81 | |||
| 82 | <p> | ||
| 83 | |||
| 84 | You read and write beans in XML format using the | ||
| 85 | {@link java.beans.XMLDecoder} | ||
| 86 | and | ||
| 87 | {@link java.beans.XMLEncoder} | ||
| 88 | classes, respectively. | ||
| 89 | One notable feature of the persistence scheme is that | ||
| 90 | reading in a bean requires no special knowledge of the bean. | ||
| 91 | |||
| 92 | <p> | ||
| 93 | Writing out a bean, on the other hand, | ||
| 94 | sometimes requires special knowledge of the bean's type. | ||
| 95 | If the bean's state can be | ||
| 96 | expressed using only the no-argument constructor and | ||
| 97 | public getter and setter methods for properties, | ||
| 98 | no special knowledge is required. | ||
| 99 | Otherwise, the bean requires a custom <em>persistence delegate</em> -- | ||
| 100 | an object that is in charge of writing out beans of a particular type. | ||
| 101 | All classes provided in the JDK that descend | ||
| 102 | from <code>java.awt.Component</code>, | ||
| 103 | as well as all their properties, | ||
| 104 | automatically have persistence delegates. | ||
| 105 | |||
| 106 | <p> | ||
| 107 | |||
| 108 | If you need (or choose) to provide a persistence delegate for a bean, | ||
| 109 | you can do so either by using a | ||
| 110 | {@link java.beans.DefaultPersistenceDelegate} | ||
| 111 | instance | ||
| 112 | or by creating your own subclass of <code>PersistenceDelegate</code>. | ||
| 113 | If the only reason a bean needs a persistence delegate | ||
| 114 | is because you want to invoke the bean's constructor with | ||
| 115 | property values as arguments, | ||
| 116 | you can create the bean's persistence delegate | ||
| 117 | with the one-argument | ||
| 118 | <code>DefaultPersistenceDelegate</code> | ||
| 119 | constructor. | ||
| 120 | Otherwise, | ||
| 121 | you need to implement your own persistence delegate, | ||
| 122 | for which you're likely to need the following classes: | ||
| 123 | |||
| 124 | <dl> | ||
| 125 | <dt> {@link java.beans.PersistenceDelegate} | ||
| 126 | <dd> The abstract class from which all persistence delegates descend. | ||
| 127 | Your subclass should use its knowledge of the bean's type to provide | ||
| 128 | whatever <code>Statement</code>s and <code>Expression</code>s | ||
| 129 | are necessary to create the bean | ||
| 130 | and restore its state. | ||
| 131 | <dt> {@link java.beans.Statement} | ||
| 132 | <dd> Represents the invocation of a single method on an object. | ||
| 133 | Includes a set of arguments to the method. | ||
| 134 | <dt> {@link java.beans.Expression} | ||
| 135 | <dd> A subclass of <code>Statement</code> | ||
| 136 | used for methods that return a value. | ||
| 137 | </dl> | ||
| 138 | |||
| 139 | <p> | ||
| 140 | Once you create a persistence delegate, | ||
| 141 | you register it using the | ||
| 142 | <code>setPersistenceDelegate</code> method of | ||
| 143 | <code>XMLEncoder</code>. | ||
| 144 | |||
| 145 | |||
| 146 | <h2>Related Documentation</h2> | ||
| 147 | |||
| 148 | For overview, architecture, and tutorial documentation, please see: | ||
| 149 | <ul> | ||
| 150 | <li><a href="http://java.sun.com/docs/books/tutorial/javabeans/">JavaBeans</a>, a trail in <em>The Java Tutorial</em>. | ||
| 151 | <li><a href="http://java.sun.com/products/jfc/tsc/articles/persistence2/">Long-Term Persistence</a>, an article in <em>The Swing Connection</em>. | ||
| 152 | </ul> | ||
| 153 | <p> | ||
| 154 | |||
| 155 | </body> | ||
| 156 | </html> | ||
Binary file not shown.