summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/java/beans
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/beans')
-rw-r--r--libjava/classpath/java/beans/DefaultPersistenceDelegate.java194
-rw-r--r--libjava/classpath/java/beans/Encoder.java463
-rw-r--r--libjava/classpath/java/beans/EventSetDescriptor.java1173
-rw-r--r--libjava/classpath/java/beans/Expression.java81
-rw-r--r--libjava/classpath/java/beans/IndexedPropertyChangeEvent.java81
-rw-r--r--libjava/classpath/java/beans/Introspector.java204
-rw-r--r--libjava/classpath/java/beans/PersistenceDelegate.java91
-rw-r--r--libjava/classpath/java/beans/PropertyChangeSupport.java52
-rw-r--r--libjava/classpath/java/beans/Statement.java165
-rw-r--r--libjava/classpath/java/beans/XMLEncoder.java265
10 files changed, 2172 insertions, 597 deletions
diff --git a/libjava/classpath/java/beans/DefaultPersistenceDelegate.java b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java
new file mode 100644
index 00000000000..9dd1ae564f7
--- /dev/null
+++ b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java
@@ -0,0 +1,194 @@
+/* DefaultPersistenceDelegate.java
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ This file is part of GNU Classpath.
+
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module. An independent module is a module which is not derived from
+ or based on this library. If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version. */
+
+
+package java.beans;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/** <p><code>DefaultPersistenceDelegate</code> is a {@link PersistenceDelegate}
+ * implementation that can be used to serialize objects which adhere to the
+ * Java Beans naming convention.</p>
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public class DefaultPersistenceDelegate extends PersistenceDelegate
+{
+
+ private String[] constructorPropertyNames;
+
+ /** Using this constructor the object to be serialized will be instantiated
+ * with the default non-argument constructor.
+ */
+ public DefaultPersistenceDelegate()
+ {
+ }
+
+ /** This constructor allows to specify which Bean properties appear
+ * in the constructor.
+ *
+ * <p>The implementation reads the mentioned properties from the Bean
+ * instance and applies it in the given order to a corresponding
+ * constructor.</p>
+ *
+ * @param constructorPropertyNames The properties the Bean's constructor
+ * should be given to.
+ */
+ public DefaultPersistenceDelegate(String[] constructorPropertyNames)
+ {
+ this.constructorPropertyNames = constructorPropertyNames;
+ }
+
+ protected boolean mutatesTo(Object oldInstance, Object newInstance)
+ {
+ try
+ {
+
+ return (constructorPropertyNames != null
+ && constructorPropertyNames.length > 0
+ && oldInstance.getClass()
+ .getDeclaredMethod("equals",
+ new Class[] { Object.class }) != null)
+ ? oldInstance.equals(newInstance)
+ : super.mutatesTo(oldInstance, newInstance);
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ return super.mutatesTo(oldInstance, newInstance);
+ }
+ }
+
+ protected Expression instantiate(Object oldInstance, Encoder out)
+ {
+ Object[] args = null;
+
+ try
+ {
+ // If there are property names in the array, then we create
+ // a corresponding argument array and store every
+ // argument in it. To retrieve an argument object we have
+ // dig up the right property in the bean class' BeanInfo
+ // object.
+ // This is so costly in terms of execution time I better
+ // not think twice about it ...
+ if (constructorPropertyNames != null)
+ {
+ args = new Object[constructorPropertyNames.length];
+
+ // Look up the properties of oldInstance's class to find matches for
+ // the
+ // names given in the constructor.
+ PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo(
+ oldInstance.getClass()).getPropertyDescriptors();
+
+ for (int i = 0; i < constructorPropertyNames.length; i++)
+ {
+ // Scan the property descriptions for a matching name.
+ for (int j = 0; j < propertyDescs.length; j++)
+ {
+ if (propertyDescs[i].getName().equals(
+ constructorPropertyNames[i]))
+ {
+ Method readMethod = propertyDescs[i].getReadMethod();
+
+ args[i] = readMethod.invoke(oldInstance, null);
+ }
+ }
+ }
+ }
+
+ }
+ catch (IllegalAccessException iae)
+ {
+ out.getExceptionListener().exceptionThrown(iae);
+ }
+ catch (IllegalArgumentException iarge)
+ {
+ out.getExceptionListener().exceptionThrown(iarge);
+ }
+ catch (InvocationTargetException ite)
+ {
+ out.getExceptionListener().exceptionThrown(ite);
+ }
+ catch (IntrospectionException ie)
+ {
+ out.getExceptionListener().exceptionThrown(ie);
+ }
+
+ return new Expression(oldInstance, oldInstance.getClass(), "new", args);
+ }
+
+ protected void initialize(Class type, Object oldInstance, Object newInstance,
+ Encoder out)
+ {
+ try
+ {
+ PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo(
+ oldInstance.getClass()).getPropertyDescriptors();
+
+ for (int i = 0; i < propertyDescs.length; i++)
+ {
+ Method readMethod = propertyDescs[i].getReadMethod();
+ Method writeMethod = propertyDescs[i].getWriteMethod();
+
+ if (readMethod != null && writeMethod != null)
+ {
+ Object oldValue = readMethod.invoke(oldInstance, null);
+
+ if (oldValue != null)
+ out.writeStatement(new Statement(oldInstance,
+ writeMethod.getName(),
+ new Object[] { oldValue }));
+ }
+ }
+ }
+ catch (IntrospectionException ie)
+ {
+ out.getExceptionListener().exceptionThrown(ie);
+ }
+ catch (IllegalAccessException iae)
+ {
+ out.getExceptionListener().exceptionThrown(iae);
+ }
+ catch (InvocationTargetException ite)
+ {
+ out.getExceptionListener().exceptionThrown(ite);
+ }
+ }
+}
diff --git a/libjava/classpath/java/beans/Encoder.java b/libjava/classpath/java/beans/Encoder.java
new file mode 100644
index 00000000000..9b96aaa8ea6
--- /dev/null
+++ b/libjava/classpath/java/beans/Encoder.java
@@ -0,0 +1,463 @@
+/* Encoder.java
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ This file is part of GNU Classpath.
+
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module. An independent module is a module which is not derived from
+ or based on this library. If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version. */
+
+
+package java.beans;
+
+import gnu.java.beans.encoder.ArrayPersistenceDelegate;
+import gnu.java.beans.encoder.ClassPersistenceDelegate;
+import gnu.java.beans.encoder.CollectionPersistenceDelegate;
+import gnu.java.beans.encoder.MapPersistenceDelegate;
+import gnu.java.beans.encoder.PrimitivePersistenceDelegate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+
+/**
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public class Encoder
+{
+
+ /**
+ * An internal DefaultPersistenceDelegate instance that is used for every
+ * class that does not a have a special special PersistenceDelegate.
+ */
+ private static PersistenceDelegate defaultPersistenceDelegate;
+
+ private static PersistenceDelegate fakePersistenceDelegate;
+
+ /**
+ * Stores the relation Class->PersistenceDelegate.
+ */
+ private static HashMap delegates = new HashMap();
+
+ /**
+ * Stores the relation oldInstance->newInstance
+ */
+ private IdentityHashMap candidates = new IdentityHashMap();
+
+ private ExceptionListener exceptionListener;
+
+ /**
+ * A simple number that is used to restrict the access to writeExpression and
+ * writeStatement. The rule is that both methods should only be used when an
+ * object is written to the stream (= writeObject). Therefore accessCounter is
+ * incremented just before the call to writeObject and decremented afterwards.
+ * Then writeStatement and writeExpression allow execution only if
+ * accessCounter is bigger than zero.
+ */
+ private int accessCounter = 0;
+
+ public Encoder()
+ {
+ setupDefaultPersistenceDelegates();
+
+ setExceptionListener(null);
+ }
+
+ /**
+ * Sets up a bunch of {@link PersistenceDelegate} instances which are needed
+ * for the basic working of a {@link Encoder}s.
+ */
+ private static void setupDefaultPersistenceDelegates()
+ {
+ synchronized (delegates)
+ {
+ if (defaultPersistenceDelegate != null)
+ return;
+
+ delegates.put(Class.class, new ClassPersistenceDelegate());
+
+ PersistenceDelegate pd = new PrimitivePersistenceDelegate();
+ delegates.put(Boolean.class, pd);
+ delegates.put(Byte.class, pd);
+ delegates.put(Short.class, pd);
+ delegates.put(Integer.class, pd);
+ delegates.put(Long.class, pd);
+ delegates.put(Float.class, pd);
+ delegates.put(Double.class, pd);
+
+ delegates.put(Object[].class, new ArrayPersistenceDelegate());
+
+ pd = new CollectionPersistenceDelegate();
+ delegates.put(ArrayList.class, pd);
+ delegates.put(LinkedList.class, pd);
+ delegates.put(Vector.class, pd);
+ delegates.put(HashSet.class, pd);
+ delegates.put(LinkedHashSet.class, pd);
+ delegates.put(TreeSet.class, pd);
+
+ pd = new MapPersistenceDelegate();
+ delegates.put(HashMap.class, pd);
+ delegates.put(TreeMap.class, pd);
+ delegates.put(java.util.Hashtable.class, pd);
+ delegates.put(java.util.IdentityHashMap.class, pd);
+
+ delegates.put(java.util.LinkedHashMap.class, pd);
+ delegates.put(java.util.Properties.class, pd);
+
+ delegates.put(java.awt.RenderingHints.class, pd);
+ delegates.put(java.util.WeakHashMap.class, pd);
+ delegates.put(javax.swing.UIDefaults.class, pd);
+
+ // TODO: These classes need to be implemented first
+ //delegates.put(java.security.AuthProvider.class, pd);
+ //delegates.put(java.util.concurrent.ConcurrentHashMap.class, pd);
+ //delegates.put(java.util.EnumMap.class, pd);
+ //delegates.put(javax.management.openmbean.TabularDataSupport.class, pd);
+
+ defaultPersistenceDelegate = new DefaultPersistenceDelegate();
+ delegates.put(Object.class, defaultPersistenceDelegate);
+
+ // Creates a PersistenceDelegate implementation which is
+ // returned for 'null'. In practice this instance is
+ // not used in any way and is just here to be compatible
+ // with the reference implementation which returns a
+ // similar instance when calling getPersistenceDelegate(null) .
+ fakePersistenceDelegate = new PersistenceDelegate()
+ {
+ protected Expression instantiate(Object o, Encoder e)
+ {
+ return null;
+ }
+ };
+
+ }
+ }
+
+ protected void writeObject(Object o)
+ {
+ // 'null' has no PersistenceDelegate and will not
+ // create an Expression which has to be cloned.
+ // However subclasses should be aware that writeObject
+ // may be called with a 'null' argument and should
+ // write the proper representation of it.
+ if (o == null)
+ return;
+
+ PersistenceDelegate pd = getPersistenceDelegate(o.getClass());
+
+ accessCounter++;
+ pd.writeObject(o, this);
+ accessCounter--;
+
+ }
+
+ /**
+ * Sets the {@link ExceptionListener} instance to be used for reporting
+ * recorable exceptions in the instantiation and initialization sequence. If
+ * the argument is <code>null</code> a default instance will be used that
+ * prints the thrown exception to <code>System.err</code>.
+ */
+ public void setExceptionListener(ExceptionListener listener)
+ {
+ exceptionListener = (listener != null) ? listener : new ExceptionListener()
+ {
+ public void exceptionThrown(Exception e)
+ {
+ System.err.println("exception thrown: " + e);
+ e.printStackTrace();
+ }
+ };
+ }
+
+ /**
+ * Returns the currently active {@link ExceptionListener} instance.
+ */
+ public ExceptionListener getExceptionListener()
+ {
+ return exceptionListener;
+ }
+
+ public PersistenceDelegate getPersistenceDelegate(Class type)
+ {
+ // This is not specified but the JDK behaves like this.
+ if (type == null)
+ return fakePersistenceDelegate;
+
+ // Treats all array classes in the same way and assigns
+ // them a shared PersistenceDelegate implementation tailored
+ // for array instantation and initialization.
+ if (type.isArray())
+ return (PersistenceDelegate) delegates.get(Object[].class);
+
+ PersistenceDelegate pd = (PersistenceDelegate) delegates.get(type);
+
+ return (pd != null) ? pd : (PersistenceDelegate) defaultPersistenceDelegate;
+ }
+
+ /**
+ * Sets the {@link PersistenceDelegate} instance for the given class.
+ * <p>
+ * Note: Throws a <code>NullPointerException</code> if the argument is
+ * <code>null</code>.
+ * </p>
+ * <p>
+ * Note: Silently ignores PersistenceDelegates for Array types and primitive
+ * wrapper classes.
+ * </p>
+ * <p>
+ * Note: Although this method is not declared <code>static</code> changes to
+ * the {@link PersistenceDelegate}s affect <strong>all</strong>
+ * {@link Encoder} instances. <strong>In this implementation</strong> the
+ * access is thread safe.
+ * </p>
+ */
+ public void setPersistenceDelegate(Class type, PersistenceDelegate delegate)
+ {
+ // If the argument is null this will cause a NullPointerException
+ // which is expected behavior.
+
+ // This makes custom PDs for array, primitive types and their wrappers
+ // impossible but this is how the JDK behaves.
+ if (type.isArray() || type.isPrimitive() || type == Boolean.class
+ || type == Byte.class || type == Short.class || type == Integer.class
+ || type == Long.class || type == Float.class || type == Double.class)
+ return;
+
+ synchronized (delegates)
+ {
+ delegates.put(type, delegate);
+ }
+
+ }
+
+ public Object remove(Object oldInstance)
+ {
+ return candidates.remove(oldInstance);
+ }
+
+ /**
+ * Returns the replacement object which has been created by the encoder during
+ * the instantiation sequence or <code>null</code> if the object has not
+ * been processed yet.
+ * <p>
+ * Note: The <code>String</code> class acts as an endpoint for the
+ * inherently recursive algorithm of the {@link Encoder}. Therefore instances
+ * of <code>String</code> will always be returned by this method. In other
+ * words the assertion: <code>
+ * assert (anyEncoder.get(anyString) == anyString)
+ * </code<
+ * will always hold.</p>
+ *
+ * <p>Note: If <code>null</code> is requested, the result will
+ * always be <code>null</code>.</p>
+ */
+ public Object get(Object oldInstance)
+ {
+ // String instances are handled in a special way.
+ // No one knows why this is not officially specified
+ // because this is a rather important design decision.
+ return (oldInstance == null) ? null :
+ (oldInstance.getClass() == String.class) ?
+ oldInstance : candidates.get(oldInstance);
+ }
+
+ /**
+ * <p>
+ * Note: If you call this method not from within an object instantiation and
+ * initialization sequence it will be silently ignored.
+ * </p>
+ */
+ public void writeStatement(Statement stmt)
+ {
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ Object target = stmt.getTarget();
+
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+ }
+
+ Object[] args = stmt.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ }
+
+ Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
+
+ try
+ {
+ newStmt.execute();
+ }
+ catch (Exception e)
+ {
+ exceptionListener.exceptionThrown(e);
+ }
+
+ }
+
+ /**
+ * <p>
+ * Note: If you call this method not from within an object instantiation and
+ * initialization sequence it will be silently ignored.
+ * </p>
+ */
+ public void writeExpression(Expression expr)
+ {
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ Object target = expr.getTarget();
+ Object value = null;
+ Object newValue = null;
+
+ try
+ {
+ value = expr.getValue();
+ }
+ catch (Exception e)
+ {
+ exceptionListener.exceptionThrown(e);
+ return;
+ }
+
+
+ newValue = get(value);
+
+ if (newValue == null)
+ {
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+
+ // May happen if exception was thrown.
+ if (newTarget == null)
+ {
+ return;
+ }
+ }
+
+ Object[] args = expr.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ }
+
+ Expression newExpr = new Expression(newTarget, expr.getMethodName(),
+ newArgs);
+
+ // Fakes the result of Class.forName(<primitiveType>) to make it possible
+ // to hand such a type to the encoding process.
+ if (value instanceof Class && ((Class) value).isPrimitive())
+ newExpr.setValue(value);
+
+ // Instantiates the new object.
+ try
+ {
+ newValue = newExpr.getValue();
+
+ candidates.put(value, newValue);
+ }
+ catch (Exception e)
+ {
+ exceptionListener.exceptionThrown(e);
+
+ return;
+ }
+
+ writeObject(value);
+
+ }
+ else if(value.getClass() == String.class || value.getClass() == Class.class)
+ {
+ writeObject(value);
+ }
+
+ }
+
+ /** Returns whether the given class is an immutable
+ * type which has to be handled differently when serializing it.
+ *
+ * <p>Immutable objects always have to be instantiated instead of
+ * modifying an existing instance.</p>
+ *
+ * @param type The class to test.
+ * @return Whether the first argument is an immutable type.
+ */
+ boolean isImmutableType(Class type)
+ {
+ return type == String.class || type == Class.class
+ || type == Integer.class || type == Boolean.class
+ || type == Byte.class || type == Short.class
+ || type == Long.class || type == Float.class
+ || type == Double.class;
+ }
+
+ /** Sets the stream candidate for a given object.
+ *
+ * @param oldObject The object given to the encoder.
+ * @param newObject The object the encoder generated.
+ */
+ void putCandidate(Object oldObject, Object newObject)
+ {
+ candidates.put(oldObject, newObject);
+ }
+
+}
diff --git a/libjava/classpath/java/beans/EventSetDescriptor.java b/libjava/classpath/java/beans/EventSetDescriptor.java
index 8624e643476..381a453031e 100644
--- a/libjava/classpath/java/beans/EventSetDescriptor.java
+++ b/libjava/classpath/java/beans/EventSetDescriptor.java
@@ -1,39 +1,39 @@
/* java.beans.EventSetDescriptor
- Copyright (C) 1998 Free Software Foundation, Inc.
+ Copyright (C) 1998, 2006 Free Software Foundation, Inc.
-This file is part of GNU Classpath.
+ This file is part of GNU Classpath.
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module. An independent module is a module which is not derived from
+ or based on this library. If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version. */
package java.beans;
@@ -45,398 +45,719 @@ import java.lang.reflect.Modifier;
import java.util.Vector;
/**
- ** EventSetDescriptor describes the hookup between an event source
- ** class and an event listener class.
- **
- ** EventSets have several attributes: the listener class, the events
- ** that can be fired to the listener (methods in the listener class), and
- ** an add and remove listener method from the event firer's class.<P>
- **
- ** The methods have these constraints on them:<P>
- ** <UL>
- ** <LI>event firing methods: must have <CODE>void</CODE> return value. Any
- ** parameters and exceptions are allowed. May be public, protected or
- ** package-protected. (Don't ask me why that is, I'm just following the spec.
- ** The only place it is even mentioned is in the Java Beans white paper, and
- ** there it is only implied.)</LI>
- ** <LI>add listener method: must have <CODE>void</CODE> return value. Must
- ** take exactly one argument, of the listener class's type. May fire either
- ** zero exceptions, or one exception of type <CODE>java.util.TooManyListenersException</CODE>.
- ** Must be public.</LI>
- ** <LI>remove listener method: must have <CODE>void</CODE> return value.
- ** Must take exactly one argument, of the listener class's type. May not
- ** fire any exceptions. Must be public.</LI>
- ** </UL>
- **
- ** A final constraint is that event listener classes must extend from EventListener.<P>
- **
- ** There are also various design patterns associated with some of the methods
- ** of construction. Those are explained in more detail in the appropriate
- ** constructors.<P>
- **
- ** <STRONG>Documentation Convention:</STRONG> for proper
- ** Internalization of Beans inside an RAD tool, sometimes there
- ** are two names for a property or method: a programmatic, or
- ** locale-independent name, which can be used anywhere, and a
- ** localized, display name, for ease of use. In the
- ** documentation I will specify different String values as
- ** either <EM>programmatic</EM> or <EM>localized</EM> to
- ** make this distinction clear.
- **
- ** @author John Keiser
- ** @since JDK1.1
- ** @version 1.1.0, 31 May 1998
- **/
-
-public class EventSetDescriptor extends FeatureDescriptor {
- private Method addListenerMethod;
- private Method removeListenerMethod;
- private Class listenerType;
- private MethodDescriptor[] listenerMethodDescriptors;
- private Method[] listenerMethods;
-
- private boolean unicast;
- private boolean inDefaultEventSet = true;
-
- /** Create a new EventSetDescriptor.
- ** This version of the constructor enforces the rules imposed on the methods
- ** described at the top of this class, as well as searching for:<P>
- ** <OL>
- ** <LI>The event-firing method must be non-private with signature
- ** <CODE>void &lt;listenerMethodName&gt;(&lt;eventSetName&gt;Event)</CODE>
- ** (where <CODE>&lt;eventSetName&gt;</CODE> has its first character capitalized
- ** by the constructor and the Event is a descendant of
- ** <CODE>java.util.EventObject</CODE>) in class <CODE>listenerType</CODE>
- ** (any exceptions may be thrown).
- ** <B>Implementation note:</B> Note that there could conceivably be multiple
- ** methods with this type of signature (example: java.util.MouseEvent vs.
- ** my.very.own.MouseEvent). In this implementation, all methods fitting the
- ** description will be put into the <CODE>EventSetDescriptor</CODE>, even
- ** though the spec says only one should be chosen (they probably weren't thinking as
- ** pathologically as I was). I don't like arbitrarily choosing things.
- ** If your class has only one such signature, as most do, you'll have no problems.</LI>
- ** <LI>The add and remove methods must be public and named
- ** <CODE>void add&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</CODE> and
- ** <CODE>void remove&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</CODE> in
- ** in class <CODE>eventSourceClass</CODE>, where
- ** <CODE>&lt;eventSetName&gt;</CODE> will have its first letter capitalized.
- ** Standard exception rules (see class description) apply.</LI>
- ** </OL>
- ** @param eventSourceClass the class containing the add/remove listener methods.
- ** @param eventSetName the programmatic name of the event set, generally starting
- ** with a lowercase letter (i.e. fooManChu instead of FooManChu). This will be used
- ** to generate the name of the event object as well as the names of the add and
- ** remove methods.
- ** @param listenerType the class containing the event firing method.
- ** @param listenerMethodName the name of the event firing method.
- ** @exception IntrospectionException if listenerType is not an EventListener,
- ** or if methods are not found or are invalid.
- **/
- public EventSetDescriptor(Class eventSourceClass,
- String eventSetName,
- Class listenerType,
- String listenerMethodName) throws IntrospectionException {
- setName(eventSetName);
- if(!java.util.EventListener.class.isAssignableFrom(listenerType)) {
- throw new IntrospectionException("Listener type is not an EventListener.");
- }
-
- String[] names = new String[1];
- names[0] = listenerMethodName;
-
- try {
- eventSetName = Character.toUpperCase(eventSetName.charAt(0)) + eventSetName.substring(1);
- } catch(StringIndexOutOfBoundsException e) {
- eventSetName = "";
- }
-
- findMethods(eventSourceClass,listenerType,names,"add"+eventSetName+"Listener","remove"+eventSetName+"Listener",eventSetName+"Event");
- this.listenerType = listenerType;
- checkAddListenerUnicast();
- if(this.removeListenerMethod.getExceptionTypes().length > 0) {
- throw new IntrospectionException("Listener remove method throws exceptions.");
- }
- }
-
- /** Create a new EventSetDescriptor.
- ** This form of the constructor allows you to specify the names of the methods and adds
- ** no new constraints on top of the rules already described at the top of the class.<P>
- **
- ** @param eventSourceClass the class containing the add and remove listener methods.
- ** @param eventSetName the programmatic name of the event set, generally starting
- ** with a lowercase letter (i.e. fooManChu instead of FooManChu).
- ** @param listenerType the class containing the event firing methods.
- ** @param listenerMethodNames the names of the even firing methods.
- ** @param addListenerMethodName the name of the add listener method.
- ** @param removeListenerMethodName the name of the remove listener method.
- ** @exception IntrospectionException if listenerType is not an EventListener
- ** or if methods are not found or are invalid.
- **/
- public EventSetDescriptor(Class eventSourceClass,
- String eventSetName,
- Class listenerType,
- String[] listenerMethodNames,
- String addListenerMethodName,
- String removeListenerMethodName) throws IntrospectionException {
- setName(eventSetName);
- if(!java.util.EventListener.class.isAssignableFrom(listenerType)) {
- throw new IntrospectionException("Listener type is not an EventListener.");
- }
-
- findMethods(eventSourceClass,listenerType,listenerMethodNames,addListenerMethodName,removeListenerMethodName,null);
- this.listenerType = listenerType;
- checkAddListenerUnicast();
- if(this.removeListenerMethod.getExceptionTypes().length > 0) {
- throw new IntrospectionException("Listener remove method throws exceptions.");
- }
- }
-
- /** Create a new EventSetDescriptor.
- ** This form of constructor allows you to explicitly say which methods do what, and
- ** no reflection is done by the EventSetDescriptor. The methods are, however,
- ** checked to ensure that they follow the rules set forth at the top of the class.
- ** @param eventSetName the programmatic name of the event set, generally starting
- ** with a lowercase letter (i.e. fooManChu instead of FooManChu).
- ** @param listenerType the class containing the listenerMethods.
- ** @param listenerMethods the event firing methods.
- ** @param addListenerMethod the add listener method.
- ** @param removeListenerMethod the remove listener method.
- ** @exception IntrospectionException if the listenerType is not an EventListener,
- ** or any of the methods are invalid.
- **/
- public EventSetDescriptor(String eventSetName,
- Class listenerType,
- Method[] listenerMethods,
- Method addListenerMethod,
- Method removeListenerMethod) throws IntrospectionException {
- setName(eventSetName);
- if(!java.util.EventListener.class.isAssignableFrom(listenerType)) {
- throw new IntrospectionException("Listener type is not an EventListener.");
- }
-
- this.listenerMethods = listenerMethods;
- this.addListenerMethod = addListenerMethod;
- this.removeListenerMethod = removeListenerMethod;
- this.listenerType = listenerType;
- checkMethods();
- checkAddListenerUnicast();
- if(this.removeListenerMethod.getExceptionTypes().length > 0) {
- throw new IntrospectionException("Listener remove method throws exceptions.");
- }
- }
-
- /** Create a new EventSetDescriptor.
- ** This form of constructor allows you to explicitly say which methods do what, and
- ** no reflection is done by the EventSetDescriptor. The methods are, however,
- ** checked to ensure that they follow the rules set forth at the top of the class.
- ** @param eventSetName the programmatic name of the event set, generally starting
- ** with a lowercase letter (i.e. fooManChu instead of FooManChu).
- ** @param listenerType the class containing the listenerMethods.
- ** @param listenerMethodDescriptors the event firing methods.
- ** @param addListenerMethod the add listener method.
- ** @param removeListenerMethod the remove listener method.
- ** @exception IntrospectionException if the listenerType is not an EventListener,
- ** or any of the methods are invalid.
- **/
- public EventSetDescriptor(String eventSetName,
- Class listenerType,
- MethodDescriptor[] listenerMethodDescriptors,
- Method addListenerMethod,
- Method removeListenerMethod) throws IntrospectionException {
- setName(eventSetName);
- if(!java.util.EventListener.class.isAssignableFrom(listenerType)) {
- throw new IntrospectionException("Listener type is not an EventListener.");
- }
-
- this.listenerMethodDescriptors = listenerMethodDescriptors;
- this.listenerMethods = new Method[listenerMethodDescriptors.length];
- for(int i=0;i<this.listenerMethodDescriptors.length;i++) {
- this.listenerMethods[i] = this.listenerMethodDescriptors[i].getMethod();
- }
-
- this.addListenerMethod = addListenerMethod;
- this.removeListenerMethod = removeListenerMethod;
- this.listenerType = listenerType;
- checkMethods();
- checkAddListenerUnicast();
- if(this.removeListenerMethod.getExceptionTypes().length > 0) {
- throw new IntrospectionException("Listener remove method throws exceptions.");
- }
- }
-
- /** Get the class that contains the event firing methods. **/
- public Class getListenerType() {
- return listenerType;
- }
-
- /** Get the event firing methods. **/
- public Method[] getListenerMethods() {
- return listenerMethods;
- }
-
- /** Get the event firing methods as MethodDescriptors. **/
- public MethodDescriptor[] getListenerMethodDescriptors() {
- if(listenerMethodDescriptors == null) {
- listenerMethodDescriptors = new MethodDescriptor[listenerMethods.length];
- for(int i=0;i<listenerMethods.length;i++) {
- listenerMethodDescriptors[i] = new MethodDescriptor(listenerMethods[i]);
- }
- }
- return listenerMethodDescriptors;
- }
-
- /** Get the add listener method. **/
- public Method getAddListenerMethod() {
- return addListenerMethod;
- }
-
- /** Get the remove listener method. **/
- public Method getRemoveListenerMethod() {
- return removeListenerMethod;
- }
-
- /** Set whether or not multiple listeners may be added.
- ** @param unicast whether or not multiple listeners may be added.
- **/
- public void setUnicast(boolean unicast) {
- this.unicast = unicast;
- }
-
- /** Get whether or not multiple listeners may be added. (Defaults to false.) **/
- public boolean isUnicast() {
- return unicast;
- }
-
- /** Set whether or not this is in the default event set.
- ** @param inDefaultEventSet whether this is in the default event set.
- **/
- public void setInDefaultEventSet(boolean inDefaultEventSet) {
- this.inDefaultEventSet = inDefaultEventSet;
- }
-
- /** Get whether or not this is in the default event set. (Defaults to true.)**/
- public boolean isInDefaultEventSet() {
- return inDefaultEventSet;
- }
-
- private void checkAddListenerUnicast() throws IntrospectionException {
- Class[] addListenerExceptions = this.addListenerMethod.getExceptionTypes();
- if(addListenerExceptions.length > 1) {
- throw new IntrospectionException("Listener add method throws too many exceptions.");
- } else if(addListenerExceptions.length == 1
- && !java.util.TooManyListenersException.class.isAssignableFrom(addListenerExceptions[0])) {
- throw new IntrospectionException("Listener add method throws too many exceptions.");
- }
- }
-
- private void checkMethods() throws IntrospectionException {
- if(!addListenerMethod.getDeclaringClass().isAssignableFrom(removeListenerMethod.getDeclaringClass())
- && !removeListenerMethod.getDeclaringClass().isAssignableFrom(addListenerMethod.getDeclaringClass())) {
- throw new IntrospectionException("add and remove listener methods do not come from the same class. This is bad.");
- }
- if(!addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)
- || addListenerMethod.getParameterTypes().length != 1
- || !listenerType.equals(addListenerMethod.getParameterTypes()[0])
- || !Modifier.isPublic(addListenerMethod.getModifiers())) {
- throw new IntrospectionException("Add Listener Method invalid.");
- }
- if(!removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE)
- || removeListenerMethod.getParameterTypes().length != 1
- || !listenerType.equals(removeListenerMethod.getParameterTypes()[0])
- || removeListenerMethod.getExceptionTypes().length > 0
- || !Modifier.isPublic(removeListenerMethod.getModifiers())) {
- throw new IntrospectionException("Remove Listener Method invalid.");
- }
-
- for(int i=0;i<listenerMethods.length;i++) {
- if(!listenerMethods[i].getReturnType().equals(java.lang.Void.TYPE)
- || Modifier.isPrivate(listenerMethods[i].getModifiers())) {
- throw new IntrospectionException("Event Method " + listenerMethods[i].getName() + " non-void or private.");
- }
- if(!listenerMethods[i].getDeclaringClass().isAssignableFrom(listenerType)) {
- throw new IntrospectionException("Event Method " + listenerMethods[i].getName() + " not from class " + listenerType.getName());
- }
- }
- }
-
- private void findMethods(Class eventSourceClass,
- Class listenerType,
- String listenerMethodNames[],
- String addListenerMethodName,
- String removeListenerMethodName,
- String absurdEventClassCheckName) throws IntrospectionException {
-
- /* Find add listener method and remove listener method. */
- Class[] listenerArgList = new Class[1];
- listenerArgList[0] = listenerType;
- try {
- this.addListenerMethod = eventSourceClass.getMethod(addListenerMethodName,listenerArgList);
- } catch(SecurityException E) {
- throw new IntrospectionException("SecurityException trying to access method " + addListenerMethodName + ".");
- } catch(NoSuchMethodException E) {
- throw new IntrospectionException("Could not find method " + addListenerMethodName + ".");
- }
-
- if(this.addListenerMethod == null || !this.addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) {
- throw new IntrospectionException("Add listener method does not exist, is not public, or is not void.");
- }
-
- try {
- this.removeListenerMethod = eventSourceClass.getMethod(removeListenerMethodName,listenerArgList);
- } catch(SecurityException E) {
- throw new IntrospectionException("SecurityException trying to access method " + removeListenerMethodName + ".");
- } catch(NoSuchMethodException E) {
- throw new IntrospectionException("Could not find method " + removeListenerMethodName + ".");
- }
- if(this.removeListenerMethod == null || !this.removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) {
- throw new IntrospectionException("Remove listener method does not exist, is not public, or is not void.");
- }
-
- /* Find the listener methods. */
- Method[] methods;
- try {
- methods = ClassHelper.getAllMethods(listenerType);
- } catch(SecurityException E) {
- throw new IntrospectionException("Security: You cannot access fields in this class.");
- }
-
- Vector chosenMethods = new Vector();
- boolean[] listenerMethodFound = new boolean[listenerMethodNames.length];
- for(int i=0;i<methods.length;i++) {
- if(Modifier.isPrivate(methods[i].getModifiers())) {
- continue;
- }
- Method currentMethod = methods[i];
- Class retval = currentMethod.getReturnType();
- if(retval.equals(java.lang.Void.TYPE)) {
- for(int j=0;j<listenerMethodNames.length;j++) {
- if(currentMethod.getName().equals(listenerMethodNames[j])
- && (absurdEventClassCheckName == null
- || (currentMethod.getParameterTypes().length == 1
- && ((currentMethod.getParameterTypes()[0]).getName().equals(absurdEventClassCheckName)
- || (currentMethod.getParameterTypes()[0]).getName().endsWith("."+absurdEventClassCheckName)
- )
- )
- )
- ) {
- chosenMethods.addElement(currentMethod);
- listenerMethodFound[j] = true;
- }
- }
- }
- }
-
- /* Make sure we found all the methods we were looking for. */
- for(int i=0;i<listenerMethodFound.length;i++) {
- if(!listenerMethodFound[i]) {
- throw new IntrospectionException("Could not find event method " + listenerMethodNames[i]);
- }
- }
-
- /* Now that we've chosen the listener methods we want, store them. */
- this.listenerMethods = new Method[chosenMethods.size()];
- for(int i=0;i<chosenMethods.size();i++) {
- this.listenerMethods[i] = (Method)chosenMethods.elementAt(i);
- }
- }
+ * EventSetDescriptor describes the hookup between an event source class and
+ * an event listener class.
+ *
+ * <p>EventSets have several attributes: the listener class,
+ * the events that can be fired to the listener (methods in the listener
+ * class), and an add and remove listener method from the event firer's
+ * class.
+ * </p>
+ *
+ * <p>
+ * The methods have these constraints on them:
+ * <ul>
+ * <li>event firing methods: must have <code>void</code> return value. Any
+ * parameters and exceptions are allowed. May be public, protected or
+ * package-protected. (Don't ask me why that is, I'm just following the spec.
+ * The only place it is even mentioned is in the Java Beans white paper, and
+ * there it is only implied.)</li>
+ *
+ * <li>add listener method: must have <code>void</code> return value. Must
+ * take exactly one argument, of the listener class's type. May fire either
+ * zero exceptions, or one exception of type
+ * <code>java.util.TooManyListenersException</code>.
+ * Must be public.</li>
+ *
+ * <li>remove listener method: must have <code>void</code> return value. Must
+ * take exactly one argument, of the listener class's type. May not fire any
+ * exceptions. Must be public.</li>
+ * </ul>
+ *
+ * <p>
+ * A final constraint is that event listener classes must extend from
+ * EventListener.
+ * </p>
+ *
+ * <p>
+ * There are also various design patterns associated with some of the methods
+ * of construction. Those are explained in more detail in the appropriate
+ * constructors.
+ * </p>
+ *
+ * <p>
+ * <strong>Documentation Convention:</strong> for proper Internalization of
+ * Beans inside an RAD tool, sometimes there are two names for a property or
+ * method: a programmatic, or locale-independent name, which can be used
+ * anywhere, and a localized, display name, for ease of use. In the
+ * documentation I will specify different String values as either
+ * <em>programmatic</em> or <em>localized</em> to make this distinction clear.
+ *
+ * @author John Keiser
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.1
+ */
+
+public class EventSetDescriptor extends FeatureDescriptor
+{
+ private Method addListenerMethod;
+
+ private Method removeListenerMethod;
+
+ private Class listenerType;
+
+ private MethodDescriptor[] listenerMethodDescriptors;
+
+ private Method[] listenerMethods;
+
+ private Method getListenerMethod;
+
+ private boolean unicast;
+
+ private boolean inDefaultEventSet = true;
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code<.
+ *
+ * <p>
+ * This version of the constructor enforces the rules imposed on the methods
+ * described at the top of this class, as well as searching for:
+ * </p>
+ *
+ * <ol>
+ * <li>
+ * The event-firing method must be non-private with signature <code>void
+ * &lt;listenerMethodName&gt;(&lt;eventSetName&gt;Event)</code> (where
+ * <code>&lt;eventSetName&gt;</code> has its first character capitalized
+ * by the constructor and the Event is a descendant of
+ * {@link java.util.EventObject}) in class <code>listenerType</code>
+ * (any exceptions may be thrown). <b>Implementation note:</b> Note that
+ * there could conceivably be multiple methods with this type of signature
+ * (example: <code>java.util.MouseEvent</code> vs.
+ * <code>my.very.own.MouseEvent</code>). In this implementation, all
+ * methods fitting the description will be put into the
+ * <code>EventSetDescriptor</code>, even though the spec says only one
+ * should be chosen (they probably weren't thinking as pathologically as I
+ * was). I don't like arbitrarily choosing things. If your class has only one
+ * such signature, as most do, you'll have no problems.</li>
+ *
+ * <li>The add and remove methods must be public and named <code>void
+ * add&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</code> and
+ * <code>void remove&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</code>
+ * in in class <code>eventSourceClass</code>, where
+ * <code>&lt;eventSetName&gt;</code> will have its first letter capitalized.
+ * Standard exception rules (see class description) apply.</li>
+ * </ol>
+ *
+ * @param eventSourceClass
+ * the class containing the add/remove listener methods.
+ * @param eventSetName
+ * the programmatic name of the event set, generally starting with a
+ * lowercase letter (i.e. fooManChu instead of FooManChu). This will
+ * be used to generate the name of the event object as well as the
+ * names of the add and remove methods.
+ * @param listenerType
+ * the class containing the event firing method.
+ * @param listenerMethodName
+ * the name of the event firing method.
+ * @exception IntrospectionException
+ * if listenerType is not an EventListener, or if methods are not
+ * found or are invalid.
+ */
+ public EventSetDescriptor(Class eventSourceClass, String eventSetName,
+ Class listenerType, String listenerMethodName)
+ throws IntrospectionException
+ {
+ setName(eventSetName);
+ if (!java.util.EventListener.class.isAssignableFrom(listenerType))
+ {
+ throw new IntrospectionException(
+ "Listener type is not an EventListener.");
+ }
+
+ String[] names = new String[1];
+ names[0] = listenerMethodName;
+
+ try
+ {
+ eventSetName = Character.toUpperCase(eventSetName.charAt(0))
+ + eventSetName.substring(1);
+ }
+ catch (StringIndexOutOfBoundsException e)
+ {
+ eventSetName = "";
+ }
+
+ findMethods(eventSourceClass, listenerType, names,
+ "add" + eventSetName + "Listener",
+ "remove" + eventSetName + "Listener", eventSetName + "Event");
+ this.listenerType = listenerType;
+ checkAddListenerUnicast();
+ if (this.removeListenerMethod.getExceptionTypes().length > 0)
+ {
+ throw new IntrospectionException(
+ "Listener remove method throws exceptions.");
+ }
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>This form of the constructor allows you to specify the names of the
+ * methods and adds no new constraints on top of the rules already described
+ * at the top of the class.
+ * </p>
+ *
+ * @param eventSourceClass
+ * the class containing the add and remove listener methods.
+ * @param eventSetName
+ * the programmatic name of the event set, generally starting with a
+ * lowercase letter (i.e. fooManChu instead of FooManChu).
+ * @param listenerType
+ * the class containing the event firing methods.
+ * @param listenerMethodNames
+ * the names of the even firing methods.
+ * @param addListenerMethodName
+ * the name of the add listener method.
+ * @param removeListenerMethodName
+ * the name of the remove listener method.
+ * @exception IntrospectionException
+ * if listenerType is not an EventListener or if methods are not
+ * found or are invalid.
+ */
+ public EventSetDescriptor(Class eventSourceClass, String eventSetName,
+ Class listenerType, String[] listenerMethodNames,
+ String addListenerMethodName,
+ String removeListenerMethodName)
+ throws IntrospectionException
+ {
+ setName(eventSetName);
+ if (!java.util.EventListener.class.isAssignableFrom(listenerType))
+ {
+ throw new IntrospectionException(
+ "Listener type is not an EventListener.");
+ }
+
+ findMethods(eventSourceClass, listenerType, listenerMethodNames,
+ addListenerMethodName, removeListenerMethodName, null);
+ this.listenerType = listenerType;
+ checkAddListenerUnicast();
+ if (this.removeListenerMethod.getExceptionTypes().length > 0)
+ {
+ throw new IntrospectionException(
+ "Listener remove method throws exceptions.");
+ }
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>
+ * This variant of the constructor allows you to specify the names of the
+ * methods and adds no new constraints on top of the rules already described
+ * at the top of the class.
+ * </p>
+ * <p>
+ * A valid GetListener method is public, flags no exceptions and has one
+ * argument which is of type <code>Class</code>
+ * {@link java.awt.Component#getListeners(Class)} is such a method.
+ * </p>
+ * <p>
+ * Note: The validity of the return value of the GetListener method is not
+ * checked.
+ * </p>
+ *
+ * @param eventSourceClass
+ * the class containing the add and remove listener methods.
+ * @param eventSetName
+ * the programmatic name of the event set, generally starting with a
+ * lowercase letter (i.e. fooManChu instead of FooManChu).
+ * @param listenerType
+ * the class containing the event firing methods.
+ * @param listenerMethodNames
+ * the names of the even firing methods.
+ * @param addListenerMethodName
+ * the name of the add listener method.
+ * @param removeListenerMethodName
+ * the name of the remove listener method.
+ * @param getListenerMethodName
+ * Name of a method which returns the array of listeners.
+ * @exception IntrospectionException
+ * if listenerType is not an EventListener or if methods are not
+ * found or are invalid.
+ * @since 1.4
+ */
+ public EventSetDescriptor(Class eventSourceClass, String eventSetName,
+ Class listenerType, String[] listenerMethodNames,
+ String addListenerMethodName,
+ String removeListenerMethodName,
+ String getListenerMethodName)
+ throws IntrospectionException
+ {
+ this(eventSourceClass, eventSetName, listenerType, listenerMethodNames,
+ addListenerMethodName, removeListenerMethodName);
+
+ Method newGetListenerMethod = null;
+
+ try
+ {
+ newGetListenerMethod
+ = eventSourceClass.getMethod(getListenerMethodName,
+ new Class[] { Class.class });
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ throw (IntrospectionException)
+ new IntrospectionException("No method named " + getListenerMethodName
+ + " in class " + listenerType
+ + " which can be used as"
+ + " getListenerMethod.").initCause(nsme);
+ }
+
+ // Note: This does not check the return value (which
+ // should be EventListener[]) but the JDK does not either.
+
+ getListenerMethod = newGetListenerMethod;
+
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor.</code>
+ *
+ * <p>
+ * This variant of the constructor allows you to specify the names of the
+ * methods and adds no new constraints on top of the rules already described
+ * at the top of the class.
+ * </p>
+ * <p>
+ * A valid GetListener method is public, flags no exceptions and has one
+ * argument which is of type <code>Class</code>
+ * {@link java.awt.Component#getListeners(Class)} is such a method.
+ * </p>
+ * <p>
+ * Note: The validity of the return value of the GetListener method is not
+ * checked.
+ * </p>
+ *
+ * @param eventSetName
+ * the programmatic name of the event set, generally starting with a
+ * lowercase letter (i.e. fooManChu instead of FooManChu).
+ * @param listenerType
+ * the class containing the listenerMethods.
+ * @param listenerMethods
+ * the event firing methods.
+ * @param addListenerMethod
+ * the add listener method.
+ * @param removeListenerMethod
+ * the remove listener method.
+ * @param getListenerMethod
+ * The method which returns an array of the listeners.
+ * @exception IntrospectionException
+ * if the listenerType is not an EventListener, or any of the
+ * methods are invalid.
+ * @since 1.4
+ */
+ public EventSetDescriptor(String eventSetName, Class listenerType,
+ Method[] listenerMethods, Method addListenerMethod,
+ Method removeListenerMethod,
+ Method getListenerMethod)
+ throws IntrospectionException
+ {
+ this(eventSetName, listenerType, listenerMethods, addListenerMethod,
+ removeListenerMethod);
+
+ // Do no checks if the getListenerMethod is null.
+ if (getListenerMethod.getParameterTypes().length != 1
+ || getListenerMethod.getParameterTypes()[0] != Class.class
+ || getListenerMethod.getExceptionTypes().length > 0
+ || !Modifier.isPublic(getListenerMethod.getModifiers()))
+ throw new IntrospectionException("GetListener method is invalid.");
+
+ // Note: This does not check the return value (which
+ // should be EventListener[]) but the JDK does not either.
+
+ this.getListenerMethod = getListenerMethod;
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>This form of constructor allows you to explicitly say which methods
+ * do what, and no reflection is done by the <code>EventSetDescriptor</code>.
+ * The methods are, however, checked to ensure that they follow the rules
+ * set forth at the top of the class.
+ *
+ * @param eventSetName
+ * the programmatic name of the event set, generally starting with a
+ * lowercase letter (i.e. fooManChu instead of FooManChu).
+ * @param listenerType
+ * the class containing the listenerMethods.
+ * @param listenerMethods
+ * the event firing methods.
+ * @param addListenerMethod
+ * the add listener method.
+ * @param removeListenerMethod
+ * the remove listener method.
+ * @exception IntrospectionException
+ * if the listenerType is not an EventListener, or any of the
+ * methods are invalid.
+ */
+ public EventSetDescriptor(String eventSetName, Class listenerType,
+ Method[] listenerMethods, Method addListenerMethod,
+ Method removeListenerMethod)
+ throws IntrospectionException
+ {
+ setName(eventSetName);
+ if (!java.util.EventListener.class.isAssignableFrom(listenerType))
+ {
+ throw new IntrospectionException(
+ "Listener type is not an EventListener.");
+ }
+
+ this.listenerMethods = listenerMethods;
+ this.addListenerMethod = addListenerMethod;
+ this.removeListenerMethod = removeListenerMethod;
+ this.listenerType = listenerType;
+ checkMethods();
+ checkAddListenerUnicast();
+ if (this.removeListenerMethod.getExceptionTypes().length > 0)
+ {
+ throw new IntrospectionException(
+ "Listener remove method throws exceptions.");
+ }
+ }
+
+ /** Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>This form of constructor allows you to explicitly say which methods do
+ * what, and no reflection is done by the <code>EventSetDescriptor</code>.
+ * The methods are, however, checked to ensure that they follow the rules
+ * set forth at the top of the class.
+ *
+ * @param eventSetName
+ * the programmatic name of the event set, generally starting with a
+ * lowercase letter (i.e. fooManChu instead of FooManChu).
+ * @param listenerType
+ * the class containing the listenerMethods.
+ * @param listenerMethodDescriptors
+ * the event firing methods.
+ * @param addListenerMethod
+ * the add listener method.
+ * @param removeListenerMethod
+ * the remove listener method.
+ * @exception IntrospectionException
+ * if the listenerType is not an EventListener, or any of the
+ * methods are invalid.
+ */
+ public EventSetDescriptor(String eventSetName, Class listenerType,
+ MethodDescriptor[] listenerMethodDescriptors,
+ Method addListenerMethod,
+ Method removeListenerMethod)
+ throws IntrospectionException
+ {
+ setName(eventSetName);
+ if (!java.util.EventListener.class.isAssignableFrom(listenerType))
+ {
+ throw new IntrospectionException(
+ "Listener type is not an EventListener.");
+ }
+
+ this.listenerMethodDescriptors = listenerMethodDescriptors;
+ this.listenerMethods = new Method[listenerMethodDescriptors.length];
+ for (int i = 0; i < this.listenerMethodDescriptors.length; i++)
+ {
+ this.listenerMethods[i]
+ = this.listenerMethodDescriptors[i].getMethod();
+ }
+
+ this.addListenerMethod = addListenerMethod;
+ this.removeListenerMethod = removeListenerMethod;
+ this.listenerType = listenerType;
+ checkMethods();
+ checkAddListenerUnicast();
+ if (this.removeListenerMethod.getExceptionTypes().length > 0)
+ {
+ throw new IntrospectionException(
+ "Listener remove method throws exceptions.");
+ }
+ }
+
+ /** Returns the class that contains the event firing methods.
+ */
+ public Class getListenerType()
+ {
+ return listenerType;
+ }
+
+ /** Returns the event firing methods.
+ */
+ public Method[] getListenerMethods()
+ {
+ return listenerMethods;
+ }
+
+ /** Returns the event firing methods as {@link MethodDescriptor}.
+ */
+ public MethodDescriptor[] getListenerMethodDescriptors()
+ {
+ if (listenerMethodDescriptors == null)
+ {
+ listenerMethodDescriptors
+ = new MethodDescriptor[listenerMethods.length];
+
+ for (int i = 0; i < listenerMethods.length; i++)
+ {
+ listenerMethodDescriptors[i]
+ = new MethodDescriptor(listenerMethods[i]);
+ }
+ }
+
+ return listenerMethodDescriptors;
+ }
+
+ /** Returns the add listener method.
+ */
+ public Method getAddListenerMethod()
+ {
+ return addListenerMethod;
+ }
+
+ /* Returns the remove listener method.
+ */
+ public Method getRemoveListenerMethod()
+ {
+ return removeListenerMethod;
+ }
+
+ /**
+ * Returns the method that retrieves the listeners or <code>null</code> if
+ * it does not exist.
+ */
+ public Method getGetListenerMethod()
+ {
+ return getListenerMethod;
+ }
+
+ /** Sets whether or not multiple listeners may be added.
+ *
+ * @param unicast
+ * whether or not multiple listeners may be added.
+ */
+ public void setUnicast(boolean unicast)
+ {
+ this.unicast = unicast;
+ }
+
+ /** Returns whether or not multiple listeners may be added.
+ * (Defaults to false.)
+ */
+ public boolean isUnicast()
+ {
+ return unicast;
+ }
+
+ /** Sets whether or not this is in the default event set.
+ *
+ * @param inDefaultEventSet
+ * whether this is in the default event set.
+ */
+ public void setInDefaultEventSet(boolean inDefaultEventSet)
+ {
+ this.inDefaultEventSet = inDefaultEventSet;
+ }
+
+ /** Returns whether or not this is in the default event set.
+ * (Defaults to true.)
+ */
+ public boolean isInDefaultEventSet()
+ {
+ return inDefaultEventSet;
+ }
+
+ private void checkAddListenerUnicast() throws IntrospectionException
+ {
+ Class[] addListenerExceptions = this.addListenerMethod.getExceptionTypes();
+ if (addListenerExceptions.length > 1)
+ {
+ throw new IntrospectionException(
+ "Listener add method throws too many exceptions.");
+ }
+ else if (addListenerExceptions.length == 1
+ && !java.util.TooManyListenersException.class
+ .isAssignableFrom(addListenerExceptions[0]))
+ {
+ throw new IntrospectionException(
+ "Listener add method throws too many exceptions.");
+ }
+ }
+
+ private void checkMethods() throws IntrospectionException
+ {
+ if (!addListenerMethod.getDeclaringClass()
+ .isAssignableFrom(removeListenerMethod.getDeclaringClass())
+ && !removeListenerMethod.getDeclaringClass()
+ .isAssignableFrom(addListenerMethod.getDeclaringClass()))
+ {
+ throw new IntrospectionException(
+ "add and remove listener methods do not come from the"
+ + " same class. This is bad.");
+ }
+ if (!addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)
+ || addListenerMethod.getParameterTypes().length != 1
+ || !listenerType.equals(addListenerMethod.getParameterTypes()[0])
+ || !Modifier.isPublic(addListenerMethod.getModifiers()))
+ {
+ throw new IntrospectionException("Add Listener Method invalid.");
+ }
+ if (!removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE)
+ || removeListenerMethod.getParameterTypes().length != 1
+ || !listenerType.equals(removeListenerMethod.getParameterTypes()[0])
+ || removeListenerMethod.getExceptionTypes().length > 0
+ || !Modifier.isPublic(removeListenerMethod.getModifiers()))
+ {
+ throw new IntrospectionException("Remove Listener Method invalid.");
+ }
+
+ for (int i = 0; i < listenerMethods.length; i++)
+ {
+ if (!listenerMethods[i].getReturnType().equals(java.lang.Void.TYPE)
+ || Modifier.isPrivate(listenerMethods[i].getModifiers()))
+ {
+ throw new IntrospectionException("Event Method "
+ + listenerMethods[i].getName()
+ + " non-void or private.");
+ }
+ if (!listenerMethods[i].getDeclaringClass()
+ .isAssignableFrom(listenerType))
+ {
+ throw new IntrospectionException("Event Method "
+ + listenerMethods[i].getName()
+ + " not from class "
+ + listenerType.getName());
+ }
+ }
+ }
+
+ private void findMethods(Class eventSourceClass, Class listenerType,
+ String listenerMethodNames[],
+ String addListenerMethodName,
+ String removeListenerMethodName,
+ String absurdEventClassCheckName)
+ throws IntrospectionException
+ {
+
+ /* Find add listener method and remove listener method. */
+ Class[] listenerArgList = new Class[1];
+ listenerArgList[0] = listenerType;
+ try
+ {
+ this.addListenerMethod
+ = eventSourceClass.getMethod(addListenerMethodName,
+ listenerArgList);
+ }
+ catch (SecurityException E)
+ {
+ throw new IntrospectionException(
+ "SecurityException trying to access method "
+ + addListenerMethodName + ".");
+ }
+ catch (NoSuchMethodException E)
+ {
+ throw new IntrospectionException("Could not find method "
+ + addListenerMethodName + ".");
+ }
+
+ if (this.addListenerMethod == null
+ || !this.addListenerMethod.getReturnType().equals(java.lang.Void.TYPE))
+ {
+ throw new IntrospectionException(
+ "Add listener method does not exist, is not public,"
+ + " or is not void.");
+ }
+
+ try
+ {
+ this.removeListenerMethod
+ = eventSourceClass.getMethod(removeListenerMethodName,
+ listenerArgList);
+ }
+ catch (SecurityException E)
+ {
+ throw new IntrospectionException(
+ "SecurityException trying to access method "
+ + removeListenerMethodName + ".");
+ }
+ catch (NoSuchMethodException E)
+ {
+ throw new IntrospectionException("Could not find method "
+ + removeListenerMethodName + ".");
+ }
+ if (this.removeListenerMethod == null
+ || !this.removeListenerMethod.getReturnType()
+ .equals(java.lang.Void.TYPE))
+ {
+ throw new IntrospectionException(
+ "Remove listener method does not exist, is not public,"
+ + " or is not void.");
+ }
+
+ /* Find the listener methods. */
+ Method[] methods;
+ try
+ {
+ methods = ClassHelper.getAllMethods(listenerType);
+ }
+ catch (SecurityException E)
+ {
+ throw new IntrospectionException(
+ "Security: You cannot access fields in this class.");
+ }
+
+ Vector chosenMethods = new Vector();
+ boolean[] listenerMethodFound = new boolean[listenerMethodNames.length];
+ for (int i = 0; i < methods.length; i++)
+ {
+ if (Modifier.isPrivate(methods[i].getModifiers()))
+ {
+ continue;
+ }
+ Method currentMethod = methods[i];
+ Class retval = currentMethod.getReturnType();
+ if (retval.equals(java.lang.Void.TYPE))
+ {
+ for (int j = 0; j < listenerMethodNames.length; j++)
+ {
+ if (currentMethod.getName().equals(listenerMethodNames[j])
+ && (absurdEventClassCheckName == null
+ || (currentMethod.getParameterTypes().length == 1
+ && ((currentMethod.getParameterTypes()[0])
+ .getName().equals(absurdEventClassCheckName)
+ || (currentMethod.getParameterTypes()[0])
+ .getName().endsWith("." + absurdEventClassCheckName)))))
+ {
+ chosenMethods.addElement(currentMethod);
+ listenerMethodFound[j] = true;
+ }
+ }
+ }
+ }
+
+ /* Make sure we found all the methods we were looking for. */
+ for (int i = 0; i < listenerMethodFound.length; i++)
+ {
+ if (!listenerMethodFound[i])
+ {
+ throw new IntrospectionException("Could not find event method "
+ + listenerMethodNames[i]);
+ }
+ }
+
+ /* Now that we've chosen the listener methods we want, store them. */
+ this.listenerMethods = new Method[chosenMethods.size()];
+ for (int i = 0; i < chosenMethods.size(); i++)
+ {
+ this.listenerMethods[i] = (Method) chosenMethods.elementAt(i);
+ }
+ }
+
}
diff --git a/libjava/classpath/java/beans/Expression.java b/libjava/classpath/java/beans/Expression.java
index d92cb7284aa..b327864d95f 100644
--- a/libjava/classpath/java/beans/Expression.java
+++ b/libjava/classpath/java/beans/Expression.java
@@ -35,16 +35,19 @@ this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
-
package java.beans;
/**
- * class Expression
- *
- * An Expression captures the execution of an object method that
- * returns a value. It stores an object, the method to call, and the
- * arguments to pass to the method.
- *
+ * <p>An Expression captures the execution of an object method
+ * that returns a value.</p>
+ *
+ * <p>It stores an object, the method to call, and the arguments to pass to
+ * the method.</p>
+ *
+ * <p>While this class can generally be used to describe method calls it is
+ * part of the XML serialization API.</p>
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
* @since 1.4
*/
public class Expression extends Statement
@@ -53,38 +56,40 @@ public class Expression extends Statement
// yet;
private static final Object UNSET = new Object();
- // The value to return. This is equal to unset until getValue is called.
+ // The value to return. This is equal to unset until getValue is called.
private Object value;
-
/**
- * Constructor
- *
- * Constructs an Expression representing the invocation of
- * object.methodName(arg[0], arg[1], ...); However, it will never
- * be executed. Instead, value will always be returned.
- *
- * @param value The value to return.
- * @param target The object to invoke the method on.
- * @param methodName The object method to invoke.
- * @param arguments An array of arguments to pass to the method.
+ * Constructor Constructs an Expression representing the invocation of
+ * object.methodName(arg[0], arg[1], ...); However, it will never be executed.
+ * Instead, value will always be returned.
+ *
+ * @param value
+ * The value to return.
+ * @param target
+ * The object to invoke the method on.
+ * @param methodName
+ * The object method to invoke.
+ * @param arguments
+ * An array of arguments to pass to the method.
*/
public Expression(Object value, Object target, String methodName,
- Object[] arguments)
+ Object[] arguments)
{
super(target, methodName, arguments);
this.value = value;
}
/**
- * Constructor
- *
- * Constructs an Expression representing the invocation of
+ * Constructor Constructs an Expression representing the invocation of
* object.methodName(arg[0], arg[1], ...);
- *
- * @param target The object to invoke the method on.
- * @param methodName The object method to invoke.
- * @param arguments An array of arguments to pass to the method.
+ *
+ * @param target
+ * The object to invoke the method on.
+ * @param methodName
+ * The object method to invoke.
+ * @param arguments
+ * An array of arguments to pass to the method.
*/
public Expression(Object target, String methodName, Object[] arguments)
{
@@ -93,15 +98,14 @@ public class Expression extends Statement
}
/**
- * Return the result of executing the method.
- *
- * If the cached value has not yet been set, the method is
- * executed in the same way as Statement.execute(), except that
- * the value is cached, and then returned. If the value has been
+ * Return the result of executing the method. If the cached value has not yet
+ * been set, the method is executed in the same way as Statement.execute(),
+ * except that the value is cached, and then returned. If the value has been
* set, it is returned without executing the method again.
- *
+ *
* @return the result of executing the method.
- * @exception Exception if an error occurs
+ * @exception Exception
+ * if an error occurs
*/
public Object getValue() throws Exception
{
@@ -112,14 +116,15 @@ public class Expression extends Statement
/**
* Set the cached value to be returned by getValue()
- *
- * @param value the value to cache and return.
+ *
+ * @param value
+ * the value to cache and return.
*/
public void setValue(Object value)
{
this.value = value;
}
-
+
/**
* Return a string representation of this expression.
*/
@@ -127,7 +132,7 @@ public class Expression extends Statement
{
String result = super.toString();
if (value != UNSET)
- return value.getClass().getName() + " " + result;
+ return value.getClass().getName() + "=" + result;
return result;
}
}
diff --git a/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java
new file mode 100644
index 00000000000..1a7306d14db
--- /dev/null
+++ b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java
@@ -0,0 +1,81 @@
+/* Indexed property change event
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.beans;
+
+/**
+ * This is like a PropertyChangeEvent, but also carries with it the
+ * index of the property which changed.
+ * @author Tom Tromey (tromey@redhat.com)
+ * @since 1.5
+ */
+public class IndexedPropertyChangeEvent extends PropertyChangeEvent
+{
+ private static final long serialVersionUID = -320227448495806870L;
+
+ /**
+ * Index of the item that was changed.
+ */
+ private int index;
+
+ /**
+ * Create a new IndexedPropertyChangeEvent.
+ * @param source the Bean containing the property
+ * @param name the property's name
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @param index the index of the element in the property which changed
+ * @throws IllegalArgumentException if source is null
+ */
+ public IndexedPropertyChangeEvent(Object source, String name,
+ Object oldValue, Object newValue,
+ int index)
+ {
+ super(source, name, oldValue, newValue);
+ this.index = index;
+ }
+
+ /**
+ * Return the index of the changed property.
+ * @return the index
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+}
diff --git a/libjava/classpath/java/beans/Introspector.java b/libjava/classpath/java/beans/Introspector.java
index 02781b46e0d..23c3cde5e3d 100644
--- a/libjava/classpath/java/beans/Introspector.java
+++ b/libjava/classpath/java/beans/Introspector.java
@@ -211,6 +211,82 @@ public class Introspector {
return cachedInfo;
}
}
+
+ /**
+ * Returns a {@BeanInfo} instance for the given Bean class where a flag
+ * controls the usage of explicit BeanInfo class to retrieve that
+ * information.
+ *
+ * <p>You have three options:</p>
+ * <p>With {@link #USE_ALL_BEANINFO} the result is the same as
+ * {@link #getBeanInfo(Class)}.</p>
+ *
+ * <p>Calling the method with <code>flag</code> set to
+ * {@link #IGNORE_IMMEDIATE_BEANINFO} will let it use all
+ * explicit BeanInfo classes for the beans superclasses
+ * but not for the bean class itself. Furthermore eventset,
+ * property and method information is retrieved by introspection
+ * if the explicit <code>BeanInfos</code> did not provide such data
+ * (ie. return <code>null</code> on {@link BeanInfo.getMethodDescriptors},
+ * {@link BeanInfo.getEventSetDescriptors} and
+ * {@link BeanInfo.getPropertyDescriptors}.)
+ * </p>
+ *
+ * <p>When the method is called with <code>flag</code< set to
+ * {@link #IGNORE_ALL_BEANINFO} all the bean data is retrieved
+ * by inspecting the class.</p>
+ *
+ * <p>Note: Any unknown value for <code>flag</code> is interpreted
+ * as {@link #IGNORE_ALL_BEANINFO}</p>.
+ *
+ * @param beanClass The class whose BeanInfo should be returned.
+ * @param flag Controls the usage of explicit <code>BeanInfo</code> classes.
+ * @return A BeanInfo object describing the class.
+ * @throws IntrospectionException If something goes wrong while retrieving
+ * the bean data.
+ */
+ public static BeanInfo getBeanInfo(Class beanClass, int flag)
+ throws IntrospectionException
+ {
+ IntrospectionIncubator ii;
+ BeanInfoEmbryo infoEmbryo;
+
+ switch(flag)
+ {
+ case USE_ALL_BEANINFO:
+ return getBeanInfo(beanClass);
+ case IGNORE_IMMEDIATE_BEANINFO:
+ Class superclass = beanClass.getSuperclass();
+ ExplicitInfo explicit = new ExplicitInfo(superclass, null);
+
+ ii = new IntrospectionIncubator();
+ if (explicit.explicitEventSetDescriptors != null)
+ ii.setEventStopClass(superclass);
+
+ if (explicit.explicitMethodDescriptors != null)
+ ii.setMethodStopClass(superclass);
+
+ if (explicit.explicitPropertyDescriptors != null)
+ ii.setPropertyStopClass(superclass);
+
+ ii.addMethods(beanClass.getMethods());
+
+ infoEmbryo = ii.getBeanInfoEmbryo();
+ merge(infoEmbryo, explicit);
+
+ infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
+
+ return infoEmbryo.getBeanInfo();
+ case IGNORE_ALL_BEANINFO:
+ default:
+ ii = new IntrospectionIncubator();
+ ii.addMethods(beanClass.getMethods());
+ infoEmbryo = ii.getBeanInfoEmbryo();
+ infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
+
+ return infoEmbryo.getBeanInfo();
+ }
+ }
/**
* Flush all of the Introspector's internal caches.
@@ -244,6 +320,69 @@ public class Introspector {
}
}
+ /** Adds all explicity given bean info data to the introspected
+ * data.
+ *
+ * @param infoEmbryo Bean info data retrieved by introspection.
+ * @param explicit Bean info data retrieved by BeanInfo classes.
+ */
+ private static void merge(BeanInfoEmbryo infoEmbryo, ExplicitInfo explicit)
+ {
+ PropertyDescriptor[] p = explicit.explicitPropertyDescriptors;
+ if(p!=null)
+ {
+ for(int i=0;i<p.length;i++)
+ {
+ if(!infoEmbryo.hasProperty(p[i]))
+ {
+ infoEmbryo.addProperty(p[i]);
+ }
+ }
+
+ // -1 should be used to denote a missing default property but
+ // for robustness reasons any value below zero is discarded.
+ // Not doing so would let Classpath fail where the JDK succeeds.
+ if(explicit.defaultProperty > -1)
+ {
+ infoEmbryo.setDefaultPropertyName(p[explicit.defaultProperty].getName());
+ }
+ }
+ EventSetDescriptor[] e = explicit.explicitEventSetDescriptors;
+ if(e!=null)
+ {
+ for(int i=0;i<e.length;i++)
+ {
+ if(!infoEmbryo.hasEvent(e[i]))
+ {
+ infoEmbryo.addEvent(e[i]);
+ }
+ }
+
+ // -1 should be used to denote a missing default event but
+ // for robustness reasons any value below zero is discarded.
+ // Not doing so would let Classpath fail where the JDK succeeds.
+ if(explicit.defaultEvent > -1)
+ {
+ infoEmbryo.setDefaultEventName(e[explicit.defaultEvent].getName());
+ }
+ }
+ MethodDescriptor[] m = explicit.explicitMethodDescriptors;
+ if(m!=null)
+ {
+ for(int i=0;i<m.length;i++)
+ {
+ if(!infoEmbryo.hasMethod(m[i]))
+ {
+ infoEmbryo.addMethod(m[i]);
+ }
+ }
+ }
+
+ infoEmbryo.setAdditionalBeanInfo(explicit.explicitBeanInfo);
+ infoEmbryo.setIcons(explicit.im);
+
+ }
+
/**
* Get the BeanInfo for class <CODE>beanClass</CODE>,
* first by looking for explicit information, next by
@@ -267,62 +406,19 @@ public class Introspector {
ii.addMethods(beanClass.getMethods());
BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo();
- PropertyDescriptor[] p = explicit.explicitPropertyDescriptors;
- if(p!=null)
- {
- for(int i=0;i<p.length;i++)
- {
- if(!currentInfo.hasProperty(p[i]))
- {
- currentInfo.addProperty(p[i]);
- }
- }
- if(explicit.defaultProperty != -1)
- {
- currentInfo.setDefaultPropertyName(p[explicit.defaultProperty].getName());
- }
- }
- EventSetDescriptor[] e = explicit.explicitEventSetDescriptors;
- if(e!=null)
- {
- for(int i=0;i<e.length;i++)
- {
- if(!currentInfo.hasEvent(e[i]))
- {
- currentInfo.addEvent(e[i]);
- }
- }
- if(explicit.defaultEvent != -1)
- {
- currentInfo.setDefaultEventName(e[explicit.defaultEvent].getName());
- }
- }
- MethodDescriptor[] m = explicit.explicitMethodDescriptors;
- if(m!=null)
- {
- for(int i=0;i<m.length;i++)
- {
- if(!currentInfo.hasMethod(m[i]))
- {
- currentInfo.addMethod(m[i]);
- }
- }
- }
- // Sets the info's BeanDescriptor to the one we extracted from the
- // explicit BeanInfo instance(s) if they contained one. Otherwise we
- // create the BeanDescriptor from scratch.
- // Note: We do not create a copy the retrieved BeanDescriptor which will allow
- // the user to modify the instance while it is cached. However this is how
- // the RI does it.
- currentInfo.setBeanDescriptor(
- (explicit.explicitBeanDescriptor == null ?
- new BeanDescriptor(beanClass, null) :
- explicit.explicitBeanDescriptor));
-
- currentInfo.setAdditionalBeanInfo(explicit.explicitBeanInfo);
- currentInfo.setIcons(explicit.im);
+ merge(currentInfo, explicit);
+ // Sets the info's BeanDescriptor to the one we extracted from the
+ // explicit BeanInfo instance(s) if they contained one. Otherwise we
+ // create the BeanDescriptor from scratch.
+ // Note: We do not create a copy the retrieved BeanDescriptor which will allow
+ // the user to modify the instance while it is cached. However this is how
+ // the RI does it.
+ currentInfo.setBeanDescriptor(
+ (explicit.explicitBeanDescriptor == null ?
+ new BeanDescriptor(beanClass, null) :
+ explicit.explicitBeanDescriptor));
return currentInfo.getBeanInfo();
}
diff --git a/libjava/classpath/java/beans/PersistenceDelegate.java b/libjava/classpath/java/beans/PersistenceDelegate.java
new file mode 100644
index 00000000000..b33cbcbed06
--- /dev/null
+++ b/libjava/classpath/java/beans/PersistenceDelegate.java
@@ -0,0 +1,91 @@
+/* java.beans.PersistenceDelegate
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.beans;
+
+/** <p>A <code>PersistenceDelegate</code> describes how a another object
+ * has to constructed and transformed in order to create a complete
+ * replicate.</p>
+ *
+ * <p>For custom classes you will need to implement
+ * <code>PersistenceDelegate</code> in a way that is suitable for them.
+ * To make use of the implementation you have to register it with an
+ * {@link Encoder} using the {Encoder#setPersistenceDelegate} method.</p>
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public abstract class PersistenceDelegate
+{
+
+ protected void initialize(Class type, Object oldInstance, Object newInstance,
+ Encoder out)
+ {
+ if (type != Object.class)
+ {
+ type = type.getSuperclass();
+
+ PersistenceDelegate pd = out.getPersistenceDelegate(
+ oldInstance.getClass().getSuperclass());
+
+ pd.initialize(type, oldInstance, newInstance, out);
+ }
+ }
+
+ public void writeObject(Object oldInstance, Encoder out)
+ {
+ Object streamCandidate = out.get(oldInstance);
+
+ if (mutatesTo(oldInstance, streamCandidate))
+ {
+ initialize(oldInstance.getClass(), oldInstance, streamCandidate, out);
+ }
+ else
+ {
+ out.remove(oldInstance);
+ out.writeExpression(instantiate(oldInstance, out));
+ }
+ }
+
+ protected boolean mutatesTo(Object oldInstance, Object newInstance)
+ {
+ return (newInstance != null)
+ && oldInstance.getClass() == newInstance.getClass();
+ }
+
+ protected abstract Expression instantiate(Object oldInstance, Encoder out);
+}
diff --git a/libjava/classpath/java/beans/PropertyChangeSupport.java b/libjava/classpath/java/beans/PropertyChangeSupport.java
index a0e64af4d29..991390b5631 100644
--- a/libjava/classpath/java/beans/PropertyChangeSupport.java
+++ b/libjava/classpath/java/beans/PropertyChangeSupport.java
@@ -397,6 +397,58 @@ public class PropertyChangeSupport implements Serializable
}
/**
+ * Fire an indexed property change event. This will only fire
+ * an event if the old and new values are not equal and not null.
+ * @param name the name of the property which changed
+ * @param index the index of the property which changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @since 1.5
+ */
+ public void fireIndexedPropertyChange(String name, int index,
+ Object oldValue, Object newValue)
+ {
+ // Argument checking is done in firePropertyChange(PropertyChangeEvent) .
+ firePropertyChange(new IndexedPropertyChangeEvent(source, name,
+ oldValue, newValue,
+ index));
+ }
+
+ /**
+ * Fire an indexed property change event. This will only fire
+ * an event if the old and new values are not equal.
+ * @param name the name of the property which changed
+ * @param index the index of the property which changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @since 1.5
+ */
+ public void fireIndexedPropertyChange(String name, int index,
+ int oldValue, int newValue)
+ {
+ if (oldValue != newValue)
+ fireIndexedPropertyChange(name, index, Integer.valueOf(oldValue),
+ Integer.valueOf(newValue));
+ }
+
+ /**
+ * Fire an indexed property change event. This will only fire
+ * an event if the old and new values are not equal.
+ * @param name the name of the property which changed
+ * @param index the index of the property which changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @since 1.5
+ */
+ public void fireIndexedPropertyChange(String name, int index,
+ boolean oldValue, boolean newValue)
+ {
+ if (oldValue != newValue)
+ fireIndexedPropertyChange(name, index, Boolean.valueOf(oldValue),
+ Boolean.valueOf(newValue));
+ }
+
+ /**
* Tell whether the specified property is being listened on or not. This
* will only return <code>true</code> if there are listeners on all
* properties or if there is a listener specifically on this property.
diff --git a/libjava/classpath/java/beans/Statement.java b/libjava/classpath/java/beans/Statement.java
index 8e916a286be..62a5ad7b6f8 100644
--- a/libjava/classpath/java/beans/Statement.java
+++ b/libjava/classpath/java/beans/Statement.java
@@ -1,4 +1,4 @@
-/* java.beans.Statement
+/* Statement.java
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -42,32 +42,26 @@ import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.WeakHashMap;
-
/**
- * class Statement
- *
- * A Statement captures the execution of an object method. It stores
+ * <p>A Statement captures the execution of an object method. It stores
* the object, the method to call, and the arguments to the method and
* provides the ability to execute the method on the object, using the
- * provided arguments.
+ * provided arguments.</p>
*
+ * @author Jerry Quinn (jlquinn@optonline.net)
+ * @author Robert Schuster (robertschuster@fsfe.org)
* @since 1.4
*/
public class Statement
{
- /** Nested map for the relation between a class, its instances and their
- * names.
- */
- private static HashMap classMaps = new HashMap();
-
private Object target;
private String methodName;
private Object[] arguments;
- // One or the other of these will get a value after execute is
- // called once, but not both.
+ /**
+ * One or the other of these will get a value after execute is
+ * called once, but not both.
+ */
private transient Method method;
private transient Constructor ctor;
@@ -87,76 +81,44 @@ public class Statement
this.target = target;
this.methodName = methodName;
this.arguments = (arguments != null) ? arguments : new Object[0];
- storeTargetName(target);
- }
-
- /** Creates a name for the target instance or does nothing if the object's
- * name is already known. This makes sure that there *is* a name for every
- * target instance.
- */
- private static synchronized void storeTargetName(Object obj)
- {
- Class klass = obj.getClass();
- WeakHashMap names = (WeakHashMap) classMaps.get(klass);
-
- if ( names == null )
- {
- names = new WeakHashMap();
-
- names.put(obj,
- ( klass == String.class ? ("\"" + obj + "\"") :
- (klass.getName() + names.size()) ));
-
- classMaps.put(klass, names);
-
- return;
- }
-
- String targetName = (String) names.get(obj);
- if ( targetName == null )
- {
- names.put(obj,
- ( klass == String.class ? ("\"" + obj + "\"") :
- (klass.getName() + names.size()) ));
- }
-
- // Nothing to do. The given object was already stored.
}
/**
* Execute the statement.
*
- * Finds the specified method in the target object and calls it with
- * the arguments given in the constructor.
+ * <p>Finds the specified method in the target object and calls it with
+ * the arguments given in the constructor.</p>
*
- * The most specific method according to the JLS(15.11) is used when
- * there are multiple methods with the same name.
+ * <p>The most specific method according to the JLS(15.11) is used when
+ * there are multiple methods with the same name.</p>
*
- * Execute performs some special handling for methods and
+ * <p>Execute performs some special handling for methods and
* parameters:
+ * <ul>
+ * <li>Static methods can be executed by providing the class as a
+ * target.</li>
*
- * Static methods can be executed by providing the class as a
- * target.
- *
- * The method name new is reserved to call the constructor
+ * <li>The method name new is reserved to call the constructor
* new() will construct an object and return it. Not useful unless
- * an expression :-)
+ * an expression :-)</li>
*
- * If the target is an array, get and set as defined in
+ * <li>If the target is an array, get and set as defined in
* java.util.List are recognized as valid methods and mapped to the
- * methods of the same name in java.lang.reflect.Array.
+ * methods of the same name in java.lang.reflect.Array.</li>
*
- * The native datatype wrappers Boolean, Byte, Character, Double,
+ * <li>The native datatype wrappers Boolean, Byte, Character, Double,
* Float, Integer, Long, and Short will map to methods that have
* native datatypes as parameters, in the same way as Method.invoke.
* However, these wrappers also select methods that actually take
- * the wrapper type as an argument.
+ * the wrapper type as an argument.</li>
+ * </ul>
+ * </p>
*
- * The Sun spec doesn't deal with overloading between int and
+ * <p>The Sun spec doesn't deal with overloading between int and
* Integer carefully. If there are two methods, one that takes an
* Integer and the other taking an int, the method chosen is not
* specified, and can depend on the order in which the methods are
- * declared in the source file.
+ * declared in the source file.</p>
*
* @throws Exception if an exception occurs while locating or
* invoking the method.
@@ -178,8 +140,10 @@ public class Statement
Integer.TYPE, Long.TYPE, Short.TYPE
};
- // Given a wrapper class, return the native class for it. For
- // example, if c is Integer, Integer.TYPE is returned.
+ /** Given a wrapper class, return the native class for it.
+ * <p>For example, if <code>c</code> is <code>Integer</code>,
+ * <code>Integer.TYPE</code> is returned.</p>
+ */
private Class unwrap(Class c)
{
for (int i = 0; i < wrappers.length; i++)
@@ -188,13 +152,22 @@ public class Statement
return null;
}
- // Return true if all args can be assigned to params, false
- // otherwise. Arrays are guaranteed to be the same length.
+ /** Returns <code>true</code> if all args can be assigned to
+ * <code>params</code>, <code>false</code> otherwise.
+ *
+ * <p>Arrays are guaranteed to be the same length.</p>
+ */
private boolean compatible(Class[] params, Class[] args)
{
for (int i = 0; i < params.length; i++)
{
- // Treat Integer like int if appropriate
+ // Argument types are derived from argument values. If one of them was
+ // null then we cannot deduce its type. However null can be assigned to
+ // any type.
+ if (args[i] == null)
+ continue;
+
+ // Treat Integer like int if appropriate
Class nativeType = unwrap(args[i]);
if (nativeType != null && params[i].isPrimitive()
&& params[i].isAssignableFrom(nativeType))
@@ -208,14 +181,15 @@ public class Statement
}
/**
- * Return true if the method arguments in first are more specific
- * than the method arguments in second, i.e. all args in first can
- * be assigned to those in second.
+ * Returns <code>true</code> if the method arguments in first are
+ * more specific than the method arguments in second, i.e. all
+ * arguments in <code>first</code> can be assigned to those in
+ * <code>second</code>.
*
- * A method is more specific if all parameters can also be fed to
+ * <p>A method is more specific if all parameters can also be fed to
* the less specific method, because, e.g. the less specific method
* accepts a base class of the equivalent argument for the more
- * specific one.
+ * specific one.</p>
*
* @param first a <code>Class[]</code> value
* @param second a <code>Class[]</code> value
@@ -238,8 +212,11 @@ public class Statement
? (Class) target : target.getClass();
Object args[] = (arguments == null) ? new Object[0] : arguments;
Class argTypes[] = new Class[args.length];
+
+ // Retrieve type or use null if the argument is null. The null argument
+ // type is later used in compatible().
for (int i = 0; i < args.length; i++)
- argTypes[i] = args[i].getClass();
+ argTypes[i] = (args[i] != null) ? args[i].getClass() : null;
if (target.getClass().isArray())
{
@@ -333,7 +310,29 @@ public class Statement
}
if (method == null)
throw new NoSuchMethodException("No matching method for statement " + toString());
+
+ // If we were calling Class.forName(String) we intercept and call the
+ // forName-variant that allows a ClassLoader argument. We take the
+ // system classloader (aka application classloader) here to make sure
+ // that application defined classes can be resolved. If we would not
+ // do that the Class.forName implementation would use the class loader
+ // of java.beans.Statement which is <null> and cannot resolve application
+ // defined classes.
+ if (method.equals(
+ Class.class.getMethod("forName", new Class[] { String.class })))
+ return Class.forName(
+ (String) args[0], true, ClassLoader.getSystemClassLoader());
+
+ try {
return method.invoke(target, args);
+ } catch(IllegalArgumentException iae){
+ System.err.println("method: " + method);
+
+ for(int i=0;i<args.length;i++){
+ System.err.println("args[" + i + "]: " + args[i]);
+ }
+ throw iae;
+ }
}
@@ -352,9 +351,13 @@ public class Statement
{
StringBuffer result = new StringBuffer();
- Class klass = target.getClass();
+ String targetName = target.getClass().getName();
+ if ( targetName.startsWith("java"))
+ {
+ targetName = targetName.substring(targetName.lastIndexOf('.') + 1);
+ }
- result.append( ((WeakHashMap) classMaps.get(klass)).get(target));
+ result.append(targetName);
result.append(".");
result.append(methodName);
result.append("(");
@@ -363,11 +366,15 @@ public class Statement
for (int i = 0; i < arguments.length; i++)
{
result.append(sep);
- result.append(arguments[i].getClass().getName());
+ result.append(
+ ( arguments[i] == null ) ? "null" :
+ ( arguments[i] instanceof String ) ? "\"" + arguments[i] + "\"" :
+ arguments[i].getClass().getName());
sep = ", ";
}
result.append(")");
return result.toString();
}
+
}
diff --git a/libjava/classpath/java/beans/XMLEncoder.java b/libjava/classpath/java/beans/XMLEncoder.java
new file mode 100644
index 00000000000..f9cbe63963d
--- /dev/null
+++ b/libjava/classpath/java/beans/XMLEncoder.java
@@ -0,0 +1,265 @@
+/* XMLEncoder.java
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+ This file is part of GNU Classpath.
+
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module. An independent module is a module which is not derived from
+ or based on this library. If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version. */
+
+
+package java.beans;
+
+import gnu.java.beans.encoder.ScanEngine;
+
+import java.io.OutputStream;
+
+/**
+ * This class uses the {@link PersistenceDelegate} and {@link Encoder}
+ * infrastructure to generate an XML representation of the objects it
+ * serializes.
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public class XMLEncoder extends Encoder
+{
+ Object owner;
+
+ Exception exception;
+
+ ScanEngine scanEngine;
+
+ private int accessCounter = 0;
+
+ public XMLEncoder(OutputStream os)
+ {
+ scanEngine = new ScanEngine(os);
+ }
+
+ public void close()
+ {
+ if (scanEngine != null)
+ {
+ scanEngine.close();
+ scanEngine = null;
+ }
+ }
+
+ public void flush()
+ {
+ scanEngine.flush();
+ }
+
+ public void writeExpression(Expression expr)
+ {
+ // Implementation note: Why is this method overwritten and nearly exactly
+ // reimplemented as in Encoder?
+ // The Encoder class can (and should be) subclassed by users outside of the
+ // java.beans package. While I have doubts that this is possible from an
+ // API design point of view I tried to replicate the Encoder's behavior
+ // in the JDK as exactly as possible. This strictness however made it
+ // extremely complicated to implement the XMLEncoder's backend. Therefore
+ // I decided to copy the Encoder's implementation and make all changes
+ // I needed for a succesfull operation of XMLEncoder.
+ //
+ // The same is true for the writeStatement method.
+
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ scanEngine.writeExpression(expr);
+
+
+ Object target = expr.getTarget();
+ Object value = null;
+ Object newValue = null;
+
+ try
+ {
+ value = expr.getValue();
+ }
+ catch (Exception e)
+ {
+ getExceptionListener().exceptionThrown(e);
+ return;
+ }
+
+
+ newValue = get(value);
+
+ if (newValue == null)
+ {
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+
+ // May happen if exception was thrown.
+ if (newTarget == null)
+ {
+ return;
+ }
+ }
+
+ Object[] args = expr.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ }
+
+ Expression newExpr = new Expression(newTarget, expr.getMethodName(),
+ newArgs);
+
+ // Fakes the result of Class.forName(<primitiveType>) to make it possible
+ // to hand such a type to the encoding process.
+ if (value instanceof Class && ((Class) value).isPrimitive())
+ newExpr.setValue(value);
+
+ // Instantiates the new object.
+ try
+ {
+ newValue = newExpr.getValue();
+
+ putCandidate(value, newValue);
+ }
+ catch (Exception e)
+ {
+ getExceptionListener().exceptionThrown(e);
+
+ // In Statement.writeExpression we had no possibility to flags
+ // an erroneous state to the ScanEngine without behaving different
+ // to the JDK.
+ scanEngine.revoke();
+ }
+
+ writeObject(value);
+
+ }
+ else if(value.getClass() == String.class || value.getClass() == Class.class)
+ {
+ writeObject(value);
+ }
+
+ scanEngine.end();
+ }
+
+ public void writeStatement(Statement stmt)
+ {
+ // In case of questions have a at the implementation note in
+ // writeExpression.
+
+ scanEngine.writeStatement(stmt);
+
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ Object target = stmt.getTarget();
+
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+ }
+
+ Object[] args = stmt.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ // Here is the difference to the original writeStatement
+ // method in Encoder. In case that the object is known or
+ // not an immutable we put it directly into the ScanEngine
+ // which will then generate an object reference for it.
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ else
+ scanEngine.writeObject(args[i]);
+ }
+
+ Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
+
+ try
+ {
+ newStmt.execute();
+ }
+ catch (Exception e)
+ {
+ getExceptionListener().exceptionThrown(e);
+
+ // In Statement.writeStatement we had no possibility to flags
+ // an erroneous state to the ScanEngine without behaving different
+ // to the JDK.
+ scanEngine.revoke();
+ return;
+ }
+
+ scanEngine.end();
+ }
+
+ public void writeObject(Object o)
+ {
+ accessCounter++;
+
+ scanEngine.writeObject(o);
+
+ if (get(o) == null);
+ super.writeObject(o);
+
+ accessCounter--;
+ }
+
+ public void setOwner(Object o)
+ {
+ owner = o;
+ }
+
+ public Object getOwner()
+ {
+ return owner;
+ }
+
+}
OpenPOWER on IntegriCloud