JavaBeans Event Handling and Properties Event Handling and Properties Handling Bean Events Event Objects, Listeners, and Sources Event Adapters Property Management Indexed, Bound and Constrained Properties Introspection - Design Patterns Explicitly Defining Bean Information Java Event Model Events Are Objects In Java JavaBeans Model based on Event Handling introduced in Java 1.1 API Event Objects Are Generated By Components for Communication between Beans Events Propagate from Source to Target Listener Objects java.util package provides base classes Event Objects Base class: java.util.EventObject public EventObject(Object source) Encapsulates information about event References Source of Event Create Event-Specific Subclasses of EventObject EventObject Example public class VoltsChangedEvent extends java.util.EventObject { protected double theVolts; public VoltsChangedEvent(Object source, double volts) { super(source); theVolts = volts; } public double getVolts() { return theVolts; } } AWTEvent Classes KeyEvent MouseEvent ContainerEvent ComponentEvent ActionEvent ItemEvent WindowEvent AdjustmentEvent PropertyChangeEvent java.beans package Sent when bound or constrained properties are altered JavaBeans must generate and send PropertyChangeEvent objects Listener Objects Listeners receive events from source objects Methods are invoked on the listener and event object is passed as a parameter To receive event notification, a class implements a listener interface Listener Interface Base class: java.util.EventListener An event fired by a source object will cause the associated method of the listener object to be called EventListener Example public interface VoltsChangeListener extends java.util.EventListener { void voltsChanged(VoltsChangedEvent e); } Some AWT Listener Interfaces & Methods ActionListener public void actionPerformed(ActionEvent e); ItemListener public void itemStateChanged(ItemEvent e); MouseListener public void mouseClicked(MouseEvent e); public void mouseEntered(MouseEvent e); public void mouseExited(MouseEvent e); public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); Java Beans Listeners java.beans.PropertyChangeListener Bound Properties public void propertyChange(PropertyChangeEvent e); java.beans.VetoableChangeListener Constrained Properties public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException; Event Sources Objects that fire events Register and Unregister Listeners Registration Design Patterns public void addEventNameListener (EventNameListener l); public void removeEventNameListener (EventNameListener l); public EventNameListener[] getEventNameListeners (); Unicast and Multicast Event Delivery Multicast - Multiple Listeners Registered with Single Event Source Unicast - Only one Listener Registered with Single Event Source Unicast Registration Design Patterns public void addEventNameListener (EventNameListener l) throws java.util.TooManyListenersException; public void removeEventNameListener (EventNameListener l); public EventNameListener getEventNameListener (); Event Source Example import java.util.Vector; public class Battery { protected double currentVolts = 12.0; private Vector voltsChangeListeners = new Vector(); public Battery(double initVoltage) { currentvolts = initVoltage; } public Battery() {} Event Source Example Continued public synchronized void addVoltsChangeListener(VoltsChangeListener l) { if (!voltsChangeListeners.contains(l)) voltsChangeListeners.addElement(l); } public synchronized void removeVoltsChangeListener(VoltsChangeListener l) { if (voltsChangeListeners.contains(l)) voltsChangeListeners.removeElement(l); } Event Source Example Continued public synchronized VoltsChangeListener[] getVoltsChangeListeners() { return (VoltsChangeListener []) voltsChangeListeners.toArray( new VoltsChangeListener[ voltsChangeListeners.size()]); } Event Source Example Continued protected void notifyVoltsChange() { VoltsChangedEvent e = new VoltsChangedEvent(this, currentVolts); Vector v; synchronized(this) { v = (Vector) voltsChangeListeners.clone();} int cnt = v.size(); for(int i = 0; i < cnt; i++) { VoltsChangeListener list = (VoltsChangeListener)v.elementAt(i); list.voltsChanged(e); } } } // delimits Class definition Registering an AWT Listener Object Use the add<event>Listener() method to register a qualified listener with a specific source object Appropriate add<event>Listener() methods are available to source components which trigger the specific events This example shows that the argument of the method is the listener object button.addActionListener(this); The Recharger Class public class Recharger implements VoltsChangeListener { protected Battery theBattery; public Recharger(Battery b) { theBattery = b; theBattery.addVoltsChangeListener(this); } public void voltsChanged(VoltsChangedEvent e) { // turn the charging switch on or off } } Event Adapters Listener code could get unmanageable Listener may want to receive several different event source objects Listener may want to listen to several objects generating the same event Handling Events with Adapters Event Source Register Listener Fire Event Event Object Event Adapter Forward Event Event Object Create Adapter Event Listener Single Instance Generic Adapter Handler1() Event Listener Handler2() Handler3() Forward Events Adapter Fire Events Event Source 1 Event Source 2 Event Source 3 Implementing a Dispatch Table using Java Reflection Target makes an instance of the Adapter Adapter discovers the target class at runtime Adapter discovers the target methods at runtime Adapter can invoke the target method Finding the Class Battery b = new Battery(12.5); Recharger rech = new Recharger(b); try { Class theClass = rech.getClass(); } catch(java.lang.ClassNotFoundException e) {} ALSO: Class theClass = Recharger.class; Finding the Method Assume a setName method for the Recharger class with the following signature: void setName(String s); Battery b = new Battery(12.5); Recharger rech = new Recharger(b); try { Class theClass = rech.getClass(); Class paramClasses[] = { java.lang.String.class }; java.lang.reflect.Method m = theClass.getMethod(“setName”,paramClasses); Object param[] = { new String(“Recharger One”) }; m.invoke(rech, param); } catch(Exception e) {} Generic Voltage Adapter Handles multiple Battery objects as event sources Maps to arbitrary target object, an instance of Recharger Dispatch methods all have the same signature: public void <methodName> (VoltsChangedEvent e); Voltage Adapter Example import java.lang.reflect.*; import java.util.*; public class GenericVoltageAdapter implements VoltsChangeListener { protected Object theTarget; protected Class theTargetClass; protected final static Class paramClass[] = { VoltsChangedEvent.class }; protected Hashtable map = new Hashtable(); public GenericVoltageAdapter(Object target) { theTarget = target; theTargetClass = target.getClass(); } Voltage Adapter Example Continued public void registerEventHandler (Battery bat, String methodName) throws NoSuchMethodException { Method m = theTargetClass.getMethod(methodName, paramClass); bat.addVoltsChangeListener(this); map.put(bat, m); } public void voltsChanged(VoltsChangedEvent e) { try { Method m = (Method)map.get(e.getSource()); Object params[] = { e }; m.invoke(theTarget, params); } catch (Exception e) {System.out.println(e);} } Revised Recharger Class public class Recharger { protected Battery battOne; protected Battery battTwo; protected GenericVoltageAdapter voltAdapter; public Recharger(Battery b1, Battery b2) { battOne = b1; battTwo = b2; voltAdapter = new GenericVoltageAdapter(this); voltAdapter.registerEventHandler(battOne, “volts1”); voltAdapter.registerEventHandler(battTwo, “volts2”); } // do something specific to battery1 changes public void volts1(VoltsChangedEvent e) { } // do something specific to battery2 changes public void volts2(VoltsChangedEvent e) { } } Properties in JavaBeans Data Portion of Bean Define Behavior and State Exposed to Visual Programming Tools for Customization Can Be Any Data Type NOT a one-to-one correspondence between Properties and data members of a class Accessor Methods Getter and Setter Methods Only Getter -> Read-only property Only Setter -> Write-only property Design Patterns: public void set<PropertyName> (<PropertyType> x); public <PropertyType> get<PropertyName>(); Boolean Property Design Pattern for Getter: public boolean is<PropertyName>(); Accessor Methods Example import java.util.Vector; public class Battery { protected double currentVolts = 12.0; private Vector voltsChangeListeners = new Vector(); // Constructors Here public double getCurrentVoltage() { return currentVolts; } public void setCurrentVoltage(double v) { currentVolts = v; // other processing } Indexed Properties Ordered collection of values associated with one name Implemented as an array Design Patterns for accessing all elements: public void set<PropertyName> (<PropertyType>[] x); public <PropertyType>[] get<PropertyName>(); Design Patterns for accessing single element: public void set<PropertyName> (int index, <PropertyType> x); public <PropertyType> get<PropertyName>(int index); Indexed Properties Example public class Recharger { protected Battery[] battArray; public void setBatteries(Battery[] b) { battArray = b; } public Battery[] getBatteries() { return battArray; } public void setBatteries(int index, Battery b) { battArray[index] = b; } public Battery getBatteries(int index) { return battArray[index]; } } Bound Properties Support change notifications Non-specific property binding PropertyChangeEvent PropertyChangeListener Design Patterns for registering and unregistering listeners for Bound Properties public void addPropertyChangeListener(PropertyChangeListener l); public void removePropertyChangeListener(PropertyChangeListener l); PropertyChangeEvent Constructor PropertyChangeEvent(Object source, String propName, Object oldValue, Object newValue); Methods public Object getNewValue(); public Object getOldValue(); public Object getPropagationId(); public String getPropertyName(); public void setPropagationId(Object propID); PropertyChangeListener public interface PropertyChangeListener extends EventListener { public void propertyChange(PropertyChangeEvent e); } Bound Property Example import java.util.Vector; import java.beans.*; public class Battery { protected double currentVolts = 12.0; protected Vector propChangeListeners = new Vector(); public Battery(double initVoltage) { currentvolts = initVoltage; } public Battery() {} Bound Property Example Continued public synchronized void addPropertyChangeListener(PropertyChangeListener l) { if (!propChangeListeners.contains(l)) propChangeListeners.addElement(l); } public synchronized void removePropertyChangeListener(PropertyChangeListener l) { if (propChangeListeners.contains(l)) propChangeListeners.removeElement(l); } Bound Property Example Continued protected void notifyVoltsChange() { PropertyChangedEvent e = new PropertyChangedEvent(this, “CurrentVoltage”, null, new Double(currentVolts)); Vector v; synchronized(this) { v = (Vector) propChangeListeners.clone();} int cnt = v.size(); for(int i = 0; i < cnt; i++) { PropertyChangeListener list = (PropertyChangeListener)v.elementAt(i); list.propertyChange(e); }} } // delimits Class definition Revised Recharger Class public class Recharger implements PropertyChangeListener { protected Battery theBattery; public Recharger(Battery b) { theBattery = b; theBattery.addPropertyChangeListener(this); } public void propertyChange(PropertyChangeEvent e) { if(e.getSource() == theBattery && e.getPropertyName() == “CurrentVoltage”) { Battery b = (Battery)e.getSource(); Object o = e.getNewValue(); double newVoltage; if ( o == null ) newVoltage = b.getCurrentVoltage(); else newVoltage = ((Double)o).doubleValue(); } }} PropertyChangeSupport import java.util.Vector; import java.beans.*; public class Battery extends PropertyChangeSupport { protected double currentVolts = 12.0; public Battery(double initVoltage){ this(); currentvolts = initVoltage; } public Battery() { super(this);} public double getCurrentVoltage() { return currentVolts; } protected void notifyVoltsChange() { firePropertyChange(“CurrentVoltage”, null, new Double(currentVolts)); } } Constrained Properties Enable Outside Party Validation VetoableChangeListener Design Patterns for registering and unregistering listeners for Constrained Properties public void addVetoableChangeListener(VetoableChangeListener l); public void removeVetoableChangeListener(VetoableChangeListener l); PropertyVetoException Design Patterns for setting and getting Constrained Properties public <PropertyType> get<PropertyName>(); public void set<PropertyName>(<PropertyType> v) throws PropertyVetoException Using Bound & Constrained Properties When Change is Vetoed, New Event must be fired Listeners cannot assume universal acceptance of constrained property Properties can be both Bound and Constrained Fire VetoableChangeEvent Change Property Value if no veto Fire PropertyChangeEvent VetoableChangeSupport Manages Constrained Properties Manages Firing of Events Constrained Property Example public class Recharger implements PropertyChangeListener { protected Battery theBattery; protected double minVoltage = 12.0; protected VetoableChangeSupport; public Recharger(Battery b) { theBattery = b; theBattery.addPropertyChangeListener(this); vSupport = new VetoableChangeSupport(this); } public void addVetoableChangeListener(VetoableChangeListener l) {vSupport.addVetoableChangeListener(l);} public void removeVetoableChangeListener(VetoableChangeListener l) {vSupport.removeVetoableChangeListener(l);} Constrained Property Example - Continued public double getMinimumVoltage() { return minVoltage; } public void setMinimumVoltage(double v) throws PropertyVetoException { if (v < 6.0) throw new PropertyVetoException(); vSupport.fireVetoableChange(“MinimumVoltage”, new Double(minVoltage), new Double(v)); minVoltage = v; } } // delimits class definition Listeners for Specific Properties Design Patterns for registering and unregistering listeners for specific Bound Properties public void add<PropertyName>Listener(PropertyChangeListener l); public void remove<PropertyName>Listener(PropertyChangeListener l); Design Patterns for registering and unregistering listeners for specific Constrained Properties public void add<PropertyName>Listener(VetoableChangeListener l); public void remove<PropertyName>Listener(VetoableChangeListener l); Introspection Beans expose information about how they work Application builder tools analyze these features Low-Level Services: Reflection Facilities High-Level Services: Limited to Public Properties, Methods, and Events Design Patterns Design Patterns Review Property Design Patterns Simple public <PropertyType> get<PropertyName>(); public void set<PropertyName>(<PropertyType> x); Boolean public boolean is<PropertyName>(); Indexed public public public public <PropertyElement> get<PropertyName>(int i); void set<PropertyName>(int i, <PropertyElement> x); <PropertyElement> [] get<PropertyName>(); void set<PropertyName>(<PropertyElement>[] x); Design Patterns Review Continued Event Design Patterns Multicast public void add<EventListenerType>(<EventListenerType> x); public void remove<EventListenerType>(<EventListenerType> x); public <EventListenerType>[] get<EventListenerType>s(); Unicast public void add<EventListenerType>(<EventListenerType> x) throws TooManyListeners; Method Design Patterns None specified Explicitly Providing Bean Information Alternative to Automatic Introspection Option for controlling Bean Information Can be used in combination with Automatic Introspection BeanInfo interface The Introspector java.beans.Introspector class Obtains Bean Information Two-tier approach Looks for explicit information Uses Reflection Techniques combined with Design Patterns The BeanInfo Interface Implemented by Bean Information Class Methods gather information about Bean Design Pattern: JavaBean class name: Car Associated BeanInfo class name: CarBeanInfo Advantage: Bean does not carry extra baggage Disadvantage: Tight Coupling Relying on Design Pattern BeanInfo Interface Methods public BeanInfo[] getAdditionalBeanInfo(): returns additional BeanInfo objects relevant to the main Bean public BeanDescriptor getBeanDescriptor(): returns Bean Descriptor public int getDefaultEventIndex(): returns default event index public int getDefaultPropertyIndex(): returns default property index public EventSetDescriptor[] getEventSetDescriptors(): returns event set descriptors public Image getIcon(int iconkind): returns specified icon public MethodDescriptor[] getMethodDescriptors(): returns method descriptors public PropertyDescriptor[] getPropertyDescriptors(): returns property descriptors SimpleBeanInfo class Implements BeanInfo interface Derive your own BeanInfo class by extending SimpleBeanInfo Additional convenience method: public Image loadImage(String resourceName); Bean Descriptor Describes class that implements the Bean Constructor: public BeanDescriptor(Class beanClass); Some FeatureDescriptor methods: public void setDisplayName(String dispName); public String getDisplayName(); Bean Descriptor Example import java.beans.*; public class RechargerBeanInfo extends SimpleBeanInfo { public BeanDescriptor getBeanDescriptor() { BeanDescriptor bd = new BeanDescriptor(Recharger.class); bd.setDisplayName(“Simulated Recharger”); return bd; } } Setting Bean Icons Icons used in Palettes or Tool Bars Four supported .GIF formats Icon 16 x 16 color 32 x 32 color 16 x 16 monochrome 32 x 32 monochrome Constant ICON_COLOR_16x16 ICON_COLOR_32x32 ICON_MONO_16x16 ICON_MONO_32x32 import java.beans.*; import java.awt.Image; Setting Bean Icons Example public class RechargerBeanInfo extends SimpleBeanInfo { public Image getIcon(int iconKind) { if(iconKind == BeanInfo.ICON_COLOR_16x16) { Image img = loadImage(“recharger.gif”); return img; } return null; } } Summary Handling Bean Events Event Objects, Listeners, and Sources Event Adapters Property Management Indexed, Bound and Constrained Properties Introspection - Design Patterns Explicitly Defining Bean Information