Author: kylem Date: Fri Dec 3 12:36:22 2004 New Revision: 109727 URL: http://svn.apache.org/viewcvs?view=rev&rev=109727 Log: Added support for receiving bound and constrained property PropertyChangeEvents as lifecycle events on ControlBeanContext. The onPropertyChange and onVetoableChange events will be delivered when bound or constrained properties are set by an external client, and provide identical semantics to registering a PropertyChangeListener or VetoableChangeListener. Extended the checkin test case to verify this behavior.
Added: incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/java/property/PropEventsTest.java Removed: incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/java/property/PropEvents.java Modified: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/context/ControlBeanContext.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEvents.java incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEventsImpl.jcs incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/contextevent/DriveBeanRecorder.java Modified: incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/context/ControlBeanContext.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/context/ControlBeanContext.java?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/context/ControlBeanContext.java&r1=109726&p2=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/context/ControlBeanContext.java&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/context/ControlBeanContext.java (original) +++ incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/context/ControlBeanContext.java Fri Dec 3 12:36:22 2004 @@ -18,6 +18,8 @@ */ import java.beans.beancontext.BeanContextServices; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; @@ -186,6 +188,23 @@ * the associated bean has been instantiated and fully initialized. */ public void onCreate(); + + /** + * The onPropertyChange event is delivered when a property setter method is + * called for a bound property on the Java Control. + * + * @see org.apache.beehive.controls.api.packaging.PropertyInfo + */ + public void onPropertyChange(PropertyChangeEvent pce); + + /** + * The onVetoableChange event is delivered when a property setter method is + * called for a constrained property on the Java Control. A PropertyVetoException + * may be thrown to veto the change made by the client. + * + * @see org.apache.beehive.controls.api.packaging.PropertyInfo + */ + public void onVetoableChange(PropertyChangeEvent pce) throws PropertyVetoException; } /** Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java&r1=109726&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBeanContext.java Fri Dec 3 12:36:22 2004 @@ -54,9 +54,12 @@ * - it acts as the BeanContextServicesRevokedListener when an associated control * bean has lost access to services. */ -public class ControlBeanContext extends BeanContextServicesSupport - implements BeanContextServiceRevokedListener, - org.apache.beehive.controls.api.context.ControlBeanContext +public class ControlBeanContext + extends BeanContextServicesSupport + implements BeanContextServiceRevokedListener, + org.apache.beehive.controls.api.context.ControlBeanContext, + java.beans.PropertyChangeListener, + java.beans.VetoableChangeListener { /** * Creates a new ControlBeanContext instance associated with a specific @@ -705,20 +708,62 @@ // // ControlBeanContext.addLifeCycleListener // - public void addLifeCycleListener(LifeCycle listener) + synchronized public void addLifeCycleListener(LifeCycle listener) { if (_lifeCycleListeners == null) + { _lifeCycleListeners = new Vector<LifeCycle>(); + + // + // Since bound/constrained property changes are exposed as lifecycle events, we + // need to register ourselves as a listener for these events the first time a + // lifecycle listener is added. + // + _bean.getPropertyChangeSupport().addPropertyChangeListener(this); + _bean.getVetoableChangeSupport().addVetoableChangeListener(this); + } _lifeCycleListeners.addElement(listener); } // // ControlBeanContext.removeLifeCycleListener // - public void removeLifeCycleListener(LifeCycle listener) + synchronized public void removeLifeCycleListener(LifeCycle listener) { if (_lifeCycleListeners != null) _lifeCycleListeners.removeElement(listener); + } + + // + // PropertyChangeListener.propertyChange + // + public void propertyChange(PropertyChangeEvent pce) + { + if (_lifeCycleListeners != null) + { + Iterator iter = _lifeCycleListeners.iterator(); + while (iter.hasNext()) + { + LifeCycle listener = (LifeCycle)iter.next(); + listener.onPropertyChange(pce); + } + } + } + + // + // VetoableChangeListener.vetoableChange + // + public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException + { + if (_lifeCycleListeners != null) + { + Iterator iter = _lifeCycleListeners.iterator(); + while (iter.hasNext()) + { + LifeCycle listener = (LifeCycle)iter.next(); + listener.onVetoableChange(pce); + } + } } /** Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java&r1=109726&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/AptEventSet.java Fri Dec 3 12:36:22 2004 @@ -17,12 +17,13 @@ * $Header:$ */ +import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; + import com.sun.mirror.apt.AnnotationProcessorEnvironment; import com.sun.mirror.declaration.InterfaceDeclaration; import com.sun.mirror.declaration.MethodDeclaration; +import com.sun.mirror.type.InterfaceType; import org.apache.beehive.controls.api.packaging.EventSetInfo; import org.apache.beehive.controls.api.packaging.FeatureInfo; @@ -49,6 +50,10 @@ _eventSet = eventSet; _env = env; + // + // TODO: Identify any super interface for this event set + // + _events = initEvents(); } @@ -62,10 +67,33 @@ return events; // - // Add all event methods to the event list + // Add all of the public methods directly declared and inherited from extended + // interfaces, except for the EventSet super interface (if any) // - for (MethodDeclaration methodDecl : _eventSet.getMethods()) - events.add(new AptEvent(this, methodDecl, _env)); + ArrayList<InterfaceDeclaration> intfList = new ArrayList<InterfaceDeclaration>(); + intfList.add(_eventSet); + for (int i = 0; i < intfList.size(); i++) + { + InterfaceDeclaration intfDecl = intfList.get(i); + + // TODO: Ignore the superinterface (if any) and continue here + + // + // Add all declared methods on the current interface + // + for (MethodDeclaration methodDecl : intfDecl.getMethods()) + events.add(new AptEvent(this, methodDecl, _env)); + + // + // Add all superinterfaces of the target interface to the list + // + for (InterfaceType superType: intfDecl.getSuperinterfaces()) + { + InterfaceDeclaration superDecl = superType.getDeclaration(); + if (superDecl != null && !intfList.contains(superDecl)) + intfList.add(superDecl); + } + } return events; } Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm&r1=109726&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBean.vm Fri Dec 3 12:36:22 2004 @@ -178,8 +178,10 @@ throws java.beans.PropertyVetoException #end { - #if ($property.bound || $property.constrained) + // Ensure the control impl exists, since it may want to receive property events + ensureControl(); + Object oldValue = getRawControlProperty($property.keyName); #end Modified: incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm&r1=109726&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm (original) +++ incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm Fri Dec 3 12:36:22 2004 @@ -66,7 +66,7 @@ public class ${bean.shortName}BeanInfo extends java.beans.SimpleBeanInfo { - #if ($intf.operations.size() != 0) + #if ($intf.operations.size() != 0 || $intf.eventSets.size() != 0) #declareMethodStatics() static Modified: incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEvents.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEvents.java?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEvents.java&r1=109726&p2=incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEvents.java&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEvents.java (original) +++ incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEvents.java Fri Dec 3 12:36:22 2004 @@ -61,11 +61,15 @@ // // These EventSets are used as an external test point for events received by the - // implementation class + // implementation class. The implementation will simply echo the ControlBeanContext + // lifecycle events it receives as events on these event sets. This enables events + // received by the Impl to be matched against those received by an externally registered + // listener. Except in veto scenarios, where someone later on the veto chain may not + // receive an event, they should be equivalent. // - //@EventSet - //public interface PropertyChangeOnImpl extends PropertyChangeListener {} + @EventSet + public interface ImplPropertyChange extends PropertyChangeListener {} - //@EventSet - //public interface VetoableChangeOnImpl extends VetoableChangeListener {} + @EventSet + public interface ImplVetoableChange extends VetoableChangeListener {} } Modified: incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEventsImpl.jcs Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEventsImpl.jcs?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEventsImpl.jcs&r1=109726&p2=incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEventsImpl.jcs&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEventsImpl.jcs (original) +++ incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropEventsImpl.jcs Fri Dec 3 12:36:22 2004 @@ -1,16 +1,42 @@ package org.apache.beehive.controls.test.controls.property; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; + import org.apache.beehive.controls.api.bean.ControlImplementation; import org.apache.beehive.controls.api.context.Context; import org.apache.beehive.controls.api.context.ControlBeanContext; +import org.apache.beehive.controls.api.events.Client; +import org.apache.beehive.controls.api.events.EventHandler; @ControlImplementation public class PropEventsImpl implements PropEvents { static final long serialVersionUID = 1L; - @Context - ControlBeanContext context; + @Context ControlBeanContext context; + + @Client ImplPropertyChange changeNotifier; + + @Client ImplVetoableChange vetoNotifier; + + // + // Receive context propertyChange events and echo them as external callbacks + // + @EventHandler(field="context", eventSet=ControlBeanContext.LifeCycle.class, + eventName="onPropertyChange") + public void onPropertyChange(PropertyChangeEvent pce) + { + changeNotifier.propertyChange(pce); + } - // Does nothing (for now) + // + // Receive context vetoableChange events and echo them as external callbacks + // + @EventHandler(field="context", eventSet=ControlBeanContext.LifeCycle.class, + eventName="onVetoableChange") + public void onVetoableChange(PropertyChangeEvent pce) throws PropertyVetoException + { + vetoNotifier.vetoableChange(pce); + } } Modified: incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/contextevent/DriveBeanRecorder.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/contextevent/DriveBeanRecorder.java?view=diff&rev=109727&p1=incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/contextevent/DriveBeanRecorder.java&r1=109726&p2=incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/contextevent/DriveBeanRecorder.java&r2=109727 ============================================================================== --- incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/contextevent/DriveBeanRecorder.java (original) +++ incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/contextevent/DriveBeanRecorder.java Fri Dec 3 12:36:22 2004 @@ -1,5 +1,6 @@ package org.apache.beehive.controls.test.driver.contextevent; +import java.beans.PropertyChangeEvent; import java.lang.Class; import org.apache.beehive.controls.api.context.ControlBeanContext; import org.apache.beehive.controls.api.context.ResourceContext; @@ -43,10 +44,13 @@ ControlBeanContext beanContext = myControl.getControlBeanContext(); beanContext.addLifeCycleListener( new ControlBeanContext.LifeCycle() - { - public void onCreate() { - onCreateReceived=true; }; - }); + { + public void onCreate() { onCreateReceived=true; }; + public void onPropertyChange(PropertyChangeEvent pce) {}; + public void onVetoableChange(PropertyChangeEvent pce) {}; + } + + ); report.setStatus(Report.PASS); } @@ -123,4 +127,4 @@ return report; } -} \ No newline at end of file +} Deleted: /incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/java/property/PropEvents.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/java/property/PropEvents.java?view=auto&rev=109726 ============================================================================== Added: incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/java/property/PropEventsTest.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/java/property/PropEventsTest.java?view=auto&rev=109727 ============================================================================== --- (empty file) +++ incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/java/property/PropEventsTest.java Fri Dec 3 12:36:22 2004 @@ -0,0 +1,365 @@ +package org.apache.beehive.controls.test.java.property; + +import java.beans.Beans; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.beehive.test.tools.mantis.annotations.tch.Freq; + +import org.apache.beehive.controls.api.bean.ControlBean; +import org.apache.beehive.controls.test.controls.property.PropEvents; +import org.apache.beehive.controls.test.controls.property.PropEventsBean; + +/** + * This test case validates bound and constrained property behaviors, w.r.t. to the delivery + * of PropertyChange events for bound and constrained events + */ [EMAIL PROTECTED]("checkin") +public class PropEventsTest extends TestCase +{ + PropEventsBean eventBean; + + public PropEventsTest( String s ) { super( s ); } + + /** + * This base class hold a queue of property change events and makes them accessible + * for event validation. + */ + abstract static class QueueListener + { + QueueListener() + { + initEvents(); + } + + /** + * Resets/initializes the internal event queue + */ + public void initEvents() + { + eventQueue = new ArrayList<PropertyChangeEvent>(); + } + + /** + * Returns the collection of events + */ + public Collection<PropertyChangeEvent> getEvents() { return eventQueue; } + + ArrayList<PropertyChangeEvent> eventQueue; + } + + static class ChangeTestListener extends QueueListener + implements PropEvents.ImplPropertyChange // + java.beans.PropertyChangeListener + { + /** + * Implementation of PropertyChangeListener.propertyChange(). Will enqueue + * the received event. + */ + public void propertyChange(PropertyChangeEvent pce) + { + eventQueue.add(pce); + } + } + + static class VetoableTestListener extends QueueListener + implements PropEvents.ImplVetoableChange // + java.beans.VetoableChangeListener + { + VetoableTestListener(boolean doVeto) + { + _doVeto = doVeto; + } + + /** + * Implementation of PropertyChangeListener.propertyChange(). Will enqueue + * the received event. + */ + public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException + { + eventQueue.add(pce); + + // Veto attempts to set even values + if (_doVeto && (((Integer)pce.getNewValue()).intValue() & 1) == 0) + throw new PropertyVetoException("Sorry", pce); + } + + boolean _doVeto; + } + + public void setUp() throws Exception + { + eventBean = (PropEventsBean) + Beans.instantiate(Thread.currentThread().getContextClassLoader(), + "org.apache.beehive.controls.test.controls.property.PropEventsBean"); + } + + private void validatePropertyEvents(Collection<PropertyChangeEvent> events) + { + // Now validate the received properties + int i = 0; + for (PropertyChangeEvent pce : events) + { + if (pce.getSource() != eventBean) + fail("Invalid source value in PropertyChangeEvent"); + + if (!pce.getPropertyName().equals("boundInt")) + fail("Invalid property name: " + pce.getPropertyName()); + + if (!pce.getOldValue().equals(new Integer(i))) + fail("Unexpected old value: " + pce.getOldValue() + ", expected: " + i); + + if (!pce.getNewValue().equals(new Integer(i+1))) + fail("Unexpected new value: " + pce.getNewValue() + ", expected: " + (i+1)); + + i++; + } + if (i != 99) + fail("Received less events than expected: " + i); + } + + /** + * Basic test: setting/reading property values by a control client + */ + public void testPropertyChange() throws Exception + { + // Set the test property to a well-defined initial value + eventBean.setBoundInt(0); + + // Create a new test listener and register it on the test bean + ChangeTestListener extChange = new ChangeTestListener(); + eventBean.addPropertyChangeListener(extChange); + + // Create an test listener and register it to indirectly from impl callbacks + ChangeTestListener implChange = new ChangeTestListener(); + eventBean.addImplPropertyChangeListener(implChange); + + // Call the bound property setter on the bean 100 times + for (int i = 1; i < 100; i++) + eventBean.setBoundInt(i); + + // Validate the events received by the external listener + validatePropertyEvents(extChange.getEvents()); + + // Validate the events received by the implementation via the context + validatePropertyEvents(implChange.getEvents()); + + // Reset the event queues + extChange.initEvents(); + implChange.initEvents(); + + // Change an unbound property and verify that no property change event was delivered + eventBean.setBasicInt(0); + if (extChange.getEvents().size() != 0) + fail("Unexpected external event delivered on unbound property change"); + if (implChange.getEvents().size() != 0) + fail("Unexpected impl event delivered on unbound property change"); + + // Remove the external event listener, change a bound property, and verify no event is + // delivered on it, but is delivered to the implentation + eventBean.removePropertyChangeListener(extChange); + eventBean.setBoundInt(0); + if (extChange.getEvents().size() != 0) + fail("Unexpected event delivered after listener removed"); + if (implChange.getEvents().size() == 0) + fail("Missing events not delivered after listener removed"); + } + + /** + * Validate the expected set of change and veto events received during the testVetoChange + * test. This is factored out so it can be used to validate events delivered to both + * the implementation and an external listener (which should match) + */ + private void validateVetoEvents(Iterator<PropertyChangeEvent> changeIter, + Iterator<PropertyChangeEvent> vetoIter) + { + // Now validate the received properties, there should be one per vetoed property, + // two per allowed change + int i = 1; + int expected = 0; + boolean expectVeto = false; + while(vetoIter.hasNext()) + { + PropertyChangeEvent vce = vetoIter.next(); + + expectVeto = (i & 1) == 0; + + if (vce.getSource() != eventBean) + fail("Invalid source value in PropertyChangeEvent"); + + if (!vce.getPropertyName().equals("constrainedInt")) + fail("Invalid property name: " + vce.getPropertyName()); + + if (!vce.getOldValue().equals(new Integer(expected))) + fail("Unexpected old value: " + vce.getOldValue() + ", expected: " + expected); + + if (!vce.getNewValue().equals(new Integer(i))) + fail("Unexpected new value: " + vce.getNewValue() + ", expected: " + i); + + if (expectVeto) + { + // If a veto occurred, then there should be a 2nd vetoable change event that + // goes from the vetoed value back to the last valid value + if (!vetoIter.hasNext()) + fail("Did not find expected veto revert event"); + + // + // Pull the next event, which should revert from the attempted change back + // to the last accepted value + // + vce = vetoIter.next(); + if (vce.getSource() != eventBean) + fail("Invalid source value in PropertyChangeEvent"); + + if (!vce.getPropertyName().equals("constrainedInt")) + fail("Invalid property name: " + vce.getPropertyName()); + + if (!vce.getOldValue().equals(new Integer(i))) + fail("Unexpected old value: " + vce.getOldValue() + ", expected: " + i); + + if (!vce.getNewValue().equals(new Integer(expected))) + fail("Unexpected new value: " + vce.getNewValue() + ", expected: " + expected); + } + else + { + // Expected to succeed so look for the corresponding PropertyChange + if (!changeIter.hasNext()) + fail("Missing PropertyChange event"); + + PropertyChangeEvent pce = changeIter.next(); + if (pce.getSource() != eventBean) + fail("Invalid source value in PropertyChangeEvent"); + + if (!pce.getPropertyName().equals("constrainedInt")) + fail("Invalid property name: " + pce.getPropertyName()); + + if (!pce.getOldValue().equals(new Integer(expected))) + fail("Unexpected old value: " + pce.getOldValue() + ", expected: " + expected); + + if (!pce.getNewValue().equals(new Integer(i))) + fail("Unexpected new value: " + pce.getNewValue() + ", expected: " + i); + + expected = i; + } + + i++; + } + if (expected != 99) + fail("Received less events than expected: " + expected); + } + + /** + * Basic test: setting/reading property values by a control client + */ + public void testVetoChange() throws Exception + { + // Set the test property to a well-defined initial value + eventBean.setConstrainedInt(0); + + // Create a test listener and register it to indirectly receive impl callbacks + // but not to veto anything + VetoableTestListener implVeto = new VetoableTestListener(false); + eventBean.addImplVetoableChangeListener(implVeto); + + // Create a test listener and register it as an external listener that will veto + VetoableTestListener extVeto = new VetoableTestListener(true); + eventBean.addVetoableChangeListener(extVeto); + + // Create an test listener and register it to indirectly from impl callbacks + ChangeTestListener implChange = new ChangeTestListener(); + eventBean.addImplPropertyChangeListener(implChange); + + // Create an external change listener and register it... this will be used to validate the + // property changes that were not vetoed + ChangeTestListener extChange = new ChangeTestListener(); + eventBean.addPropertyChangeListener(extChange); + + // + // Change the property multiple times, validating that veto exceptions propogate as + // expected and that the retrieved property value matches the expected value (whether + // accepted or vetoed) + // + int expected = 0; + for (int i = 1; i < 100; i++) + { + boolean vetoed = false; + boolean expectVeto = (i & 1) == 0; + try + { + eventBean.setConstrainedInt(i); + } + catch (PropertyVetoException pve) + { + vetoed = true; + } + + if (vetoed) + { + if (!expectVeto) + fail("Unexpected PropertyVetoException: " + i); + } + else + { + if (expectVeto) + fail("Did not receive expected PropertVetoException: " + i); + + expected = i; + } + + // + // Read back the property and see if it was successfully changed or vetoed + if (eventBean.getConstrainedInt() != expected) + fail("Did not get expected value: " + expected + " for " + i); + } + + // Validate the events generated on the implementation + validateVetoEvents(implChange.getEvents().iterator(), implVeto.getEvents().iterator()); + + // Validate the events generated on the external listener + validateVetoEvents(extChange.getEvents().iterator(), extVeto.getEvents().iterator()); + + // Reset the event queues + extVeto.initEvents(); + extChange.initEvents(); + implVeto.initEvents(); + implChange.initEvents(); + + // + // Change an unbound property and verify that no property change events were delivered + // + eventBean.setBasicInt(0); + + if (implVeto.getEvents().size() != 0 || implChange.getEvents().size() != 0) + fail("Unexpected impl event delivered on unbound property change"); + + if (extVeto.getEvents().size() != 0 || extChange.getEvents().size() != 0) + fail("Unexpected external event delivered on unbound property change"); + + // + // Remove the external veto event listener but not the impl veto listener change listener, + // change a constrained property, and verify no external veto event is delivered but an + // impl veto and external change event is delivered + // + eventBean.removeVetoableChangeListener(extVeto); + eventBean.removeImplPropertyChangeListener(implChange); + eventBean.setConstrainedInt(1); + + if (extVeto.getEvents().size() != 0) + fail("Unexpected external event delivered after listener removed"); + + if (implVeto.getEvents().size() == 0) + fail("No impl event delivered after external listener removed"); + + if (extChange.getEvents().size() != 1) + fail("External change event not delivered after listener removed"); + + if (implChange.getEvents().size() != 0) + fail("Unexpected Impl change event delivered after listener removed"); + } +}
