This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 6e2741da4 [LANG-1727] EventListenerSupport doesn't document ordering 
of events
     new b02333396 Merge branch 'master' of 
https://garydgreg...@github.com/apache/commons-lang.git
6e2741da4 is described below

commit 6e2741da4d1ef471c7c552b91409b2deecbfcec9
Author: Gary D. Gregory <garydgreg...@gmail.com>
AuthorDate: Thu Jun 19 07:35:45 2025 -0400

    [LANG-1727] EventListenerSupport doesn't document ordering of events
    
    - Javadoc
    - Use longer lines
    - Remove some vertical whitespace
---
 src/changes/changes.xml                            |  1 +
 .../commons/lang3/event/EventListenerSupport.java  | 97 ++++++++++------------
 .../lang3/event/EventListenerSupportTest.java      | 34 +++-----
 3 files changed, 60 insertions(+), 72 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9bfba7512..0d52348fd 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -94,6 +94,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action issue="LANG-1772" type="fix" dev="ggregory" due-to="Gary 
Gregory">Reimplement org.apache.commons.lang3.ClassUtils.hierarchy(Class, 
Interfaces) using an AtomicReference.</action>
     <action                   type="fix" dev="ggregory" due-to="Ken 
Dombeck">Fix Javadoc code examples in DiffBuilder and ReflectionDiffBuilder 
#1400.</action>
     <action                   type="fix" dev="ggregory" due-to="Gary 
Gregory">Fix generics in org.apache.commons.lang3.stream.Streams.toArray(Class) 
signature.</action>
+    <action issue="LANG-1727" type="fix" dev="ggregory" due-to="Elliotte Rusty 
Harold, Gary Gregory">EventListenerSupport doesn't document ordering of 
events.</action>
     <!-- ADD -->
     <action                   type="add" dev="ggregory" due-to="Gary 
Gregory">Add Strings and refactor StringUtils.</action>
     <action issue="LANG-1747" type="add" dev="ggregory" due-to="Oliver B. 
Fischer, Gary Gregory">Add StopWatch.run([Failable]Runnable) and 
get([Failable]Supplier).</action>
diff --git 
a/src/main/java/org/apache/commons/lang3/event/EventListenerSupport.java 
b/src/main/java/org/apache/commons/lang3/event/EventListenerSupport.java
index 303d2583e..6d67bfc8c 100644
--- a/src/main/java/org/apache/commons/lang3/event/EventListenerSupport.java
+++ b/src/main/java/org/apache/commons/lang3/event/EventListenerSupport.java
@@ -37,32 +37,28 @@
 import org.apache.commons.lang3.function.FailableConsumer;
 
 /**
- * An EventListenerSupport object can be used to manage a list of event
- * listeners of a particular type. The class provides
- * {@link #addListener(Object)} and {@link #removeListener(Object)} methods
- * for registering listeners, as well as a {@link #fire()} method for firing
- * events to the listeners.
+ * Manages a list of event listeners of a given generic type. This class 
provides {@link #addListener(Object)} and {@link #removeListener(Object)} 
methods for
+ * managing listeners, as well as a {@link #fire()} method for firing events 
to the listeners.
  *
  * <p>
- * To use this class, suppose you want to support ActionEvents.  You would do:
+ * For example, to support ActionEvents:
  * </p>
+ *
  * <pre>{@code
- * public class MyActionEventSource
- * {
- *   private EventListenerSupport<ActionListener> actionListeners =
- *       EventListenerSupport.create(ActionListener.class);
+ * public class MyActionEventSource {
+ *
+ *     private EventListenerSupport<ActionListener> actionListeners = 
EventListenerSupport.create(ActionListener.class);
  *
- *   public void someMethodThatFiresAction()
- *   {
- *     ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, 
"somethingCool");
- *     actionListeners.fire().actionPerformed(e);
- *   }
+ *     public void someMethodThatFiresAction() {
+ *         ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, 
"something");
+ *         actionListeners.fire().actionPerformed(e);
+ *     }
  * }
  * }</pre>
- *
  * <p>
- * Serializing an {@link EventListenerSupport} instance will result in any
- * non-{@link Serializable} listeners being silently dropped.
+ * Events are fired
+ * <p>
+ * Serializing an {@link EventListenerSupport} instance will result in any 
non-{@link Serializable} listeners being silently dropped.
  * </p>
  *
  * @param <L> the type of event listener that is supported by this proxy.
@@ -71,7 +67,7 @@
 public class EventListenerSupport<L> implements Serializable {
 
     /**
-     * An invocation handler used to dispatch the event(s) to all the 
listeners.
+     * Invokes listeners through {@link #invoke(Object, Method, Object[])} in 
the order added to the underlying {@link List}.
      */
     protected class ProxyInvocationHandler implements InvocationHandler {
 
@@ -109,6 +105,9 @@ protected void handle(final Throwable t) throws 
IllegalAccessException, IllegalA
 
         /**
          * Propagates the method call to all registered listeners in place of 
the proxy listener object.
+         * <p>
+         * Calls listeners in the order added to the underlying {@link List}.
+         * </p>
          *
          * @param unusedProxy the proxy object representing a listener on 
which the invocation was called; not used
          * @param method the listener method that will be called on all of the 
listeners.
@@ -156,18 +155,14 @@ public static <T> EventListenerSupport<T> create(final 
Class<T> listenerInterfac
     }
 
     /**
-     * The list used to hold the registered listeners. This list is
-     * intentionally a thread-safe copy-on-write-array so that traversals over
-     * the list of listeners will be atomic.
+     * Hold the registered listeners. This list is intentionally a thread-safe 
copy-on-write-array so that traversals over the list of listeners will be 
atomic.
      */
     private List<L> listeners = new CopyOnWriteArrayList<>();
 
     /**
-     * The proxy representing the collection of listeners. Calls to this proxy
-     * object will be sent to all registered listeners.
+     * The proxy representing the collection of listeners. Calls to this proxy 
object will be sent to all registered listeners.
      */
     private transient L proxy;
-
     /**
      * Empty typed array for #getListeners().
      */
@@ -175,13 +170,15 @@ public static <T> EventListenerSupport<T> create(final 
Class<T> listenerInterfac
 
     /**
      * Constructs a new EventListenerSupport instance.
-     * Serialization-friendly constructor.
+     * <p>
+     * This constructor is needed for serialization.
+     * </p>
      */
     private EventListenerSupport() {
     }
 
     /**
-     * Creates an EventListenerSupport object which supports the provided
+     * Constructs an EventListenerSupport object which supports the provided
      * listener interface.
      *
      * @param listenerInterface the type of listener interface that will 
receive
@@ -197,7 +194,7 @@ public EventListenerSupport(final Class<L> 
listenerInterface) {
     }
 
     /**
-     * Creates an EventListenerSupport object which supports the provided
+     * Constructs an EventListenerSupport object which supports the provided
      * listener interface using the specified class loader to create the JDK
      * dynamic proxy.
      *
@@ -212,29 +209,31 @@ public EventListenerSupport(final Class<L> 
listenerInterface, final ClassLoader
         this();
         Objects.requireNonNull(listenerInterface, "listenerInterface");
         Objects.requireNonNull(classLoader, "classLoader");
-        Validate.isTrue(listenerInterface.isInterface(), "Class %s is not an 
interface",
-                listenerInterface.getName());
+        Validate.isTrue(listenerInterface.isInterface(), "Class %s is not an 
interface", listenerInterface.getName());
         initializeTransientFields(listenerInterface, classLoader);
     }
 
     /**
-     * Registers an event listener.
+     * Adds an event listener.
+     * <p>
+     * Listeners are called in the order added.
+     * </p>
      *
      * @param listener the event listener (may not be {@code null}).
-     * @throws NullPointerException if {@code listener} is
-     *         {@code null}.
+     * @throws NullPointerException if {@code listener} is {@code null}.
      */
     public void addListener(final L listener) {
         addListener(listener, true);
     }
 
     /**
-     * Registers an event listener. Will not add a pre-existing listener
-     * object to the list if {@code allowDuplicate} is false.
+     * Adds an event listener. Will not add a pre-existing listener object to 
the list if {@code allowDuplicate} is false.
+     * <p>
+     * Listeners are called in the order added.
+     * </p>
      *
-     * @param listener the event listener (may not be {@code null}).
-     * @param allowDuplicate the flag for determining if duplicate listener
-     * objects are allowed to be registered.
+     * @param listener       the event listener (may not be {@code null}).
+     * @param allowDuplicate the flag for determining if duplicate listener 
objects are allowed to be registered.
      *
      * @throws NullPointerException if {@code listener} is {@code null}.
      * @since 3.5
@@ -247,7 +246,7 @@ public void addListener(final L listener, final boolean 
allowDuplicate) {
     }
 
     /**
-     * Creates the {@link InvocationHandler} responsible for broadcasting calls
+     * Creates the {@link InvocationHandler} responsible for calling
      * to the managed listeners. Subclasses can override to provide custom 
behavior.
      *
      * @return ProxyInvocationHandler
@@ -263,8 +262,7 @@ protected InvocationHandler createInvocationHandler() {
      * @param classLoader the class loader to be used
      */
     private void createProxy(final Class<L> listenerInterface, final 
ClassLoader classLoader) {
-        proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader,
-                new Class[] { listenerInterface }, createInvocationHandler()));
+        proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader, new 
Class[] { listenerInterface }, createInvocationHandler()));
     }
 
     /**
@@ -311,7 +309,7 @@ private void initializeTransientFields(final Class<L> 
listenerInterface, final C
     }
 
     /**
-     * Deserializes.
+     * Deserializes the next object into this instance.
      *
      * @param objectInputStream the input stream
      * @throws IOException if an IO error occurs
@@ -326,26 +324,25 @@ private void readObject(final ObjectInputStream 
objectInputStream) throws IOExce
     }
 
     /**
-     * Unregisters an event listener.
+     * Removes an event listener.
      *
      * @param listener the event listener (may not be {@code null}).
      * @throws NullPointerException if {@code listener} is
      *         {@code null}.
      */
     public void removeListener(final L listener) {
-        Objects.requireNonNull(listener, "listener");
-        listeners.remove(listener);
+        listeners.remove(Objects.requireNonNull(listener, "listener"));
     }
 
     /**
-     * Serializes.
+     * Serializes this instance onto the given ObjectOutputStream.
      *
      * @param objectOutputStream the output stream
      * @throws IOException if an IO error occurs
      */
     private void writeObject(final ObjectOutputStream objectOutputStream) 
throws IOException {
         final ArrayList<L> serializableListeners = new ArrayList<>();
-        // don't just rely on instanceof Serializable:
+        // Don't just rely on instanceof Serializable:
         ObjectOutputStream testObjectOutputStream = new ObjectOutputStream(new 
ByteArrayOutputStream());
         for (final L listener : listeners) {
             try {
@@ -356,10 +353,8 @@ private void writeObject(final ObjectOutputStream 
objectOutputStream) throws IOE
                 testObjectOutputStream = new ObjectOutputStream(new 
ByteArrayOutputStream());
             }
         }
-        /*
-         * we can reconstitute everything we need from an array of our 
listeners,
-         * which has the additional advantage of typically requiring less 
storage than a list:
-         */
+        // We can reconstitute everything we need from an array of our 
listeners,
+        // which has the additional advantage of typically requiring less 
storage than a list:
         
objectOutputStream.writeObject(serializableListeners.toArray(prototypeArray));
     }
 }
diff --git 
a/src/test/java/org/apache/commons/lang3/event/EventListenerSupportTest.java 
b/src/test/java/org/apache/commons/lang3/event/EventListenerSupportTest.java
index ac310b315..dfa56ccae 100644
--- a/src/test/java/org/apache/commons/lang3/event/EventListenerSupportTest.java
+++ b/src/test/java/org/apache/commons/lang3/event/EventListenerSupportTest.java
@@ -47,11 +47,13 @@
 import org.junit.jupiter.api.Test;
 
 /**
+ * Tests {@link EventListenerSupport}.
  */
 class EventListenerSupportTest extends AbstractLangTest {
 
     private void addDeregisterListener(final 
EventListenerSupport<VetoableChangeListener> listenerSupport) {
         listenerSupport.addListener(new VetoableChangeListener() {
+
             @Override
             public void vetoableChange(final PropertyChangeEvent e) {
                 listenerSupport.removeListener(this);
@@ -61,6 +63,7 @@ public void vetoableChange(final PropertyChangeEvent e) {
 
     private VetoableChangeListener createListener(final 
List<VetoableChangeListener> calledListeners) {
         return new VetoableChangeListener() {
+
             @Override
             public void vetoableChange(final PropertyChangeEvent e) {
                 calledListeners.add(this);
@@ -71,14 +74,12 @@ public void vetoableChange(final PropertyChangeEvent e) {
     @Test
     void testAddListenerNoDuplicates() {
         final EventListenerSupport<VetoableChangeListener> listenerSupport = 
EventListenerSupport.create(VetoableChangeListener.class);
-
         final VetoableChangeListener[] listeners = 
listenerSupport.getListeners();
         assertEquals(0, listeners.length);
         assertEquals(VetoableChangeListener.class, 
listeners.getClass().getComponentType());
         final VetoableChangeListener[] empty = listeners;
-        //for fun, show that the same empty instance is used
+        // for fun, show that the same empty instance is used
         assertSame(empty, listenerSupport.getListeners());
-
         final VetoableChangeListener listener1 = 
EasyMock.createNiceMock(VetoableChangeListener.class);
         listenerSupport.addListener(listener1);
         assertEquals(1, listenerSupport.getListeners().length);
@@ -108,7 +109,6 @@ void testCreateWithNullParameter() {
     void testEventDispatchOrder() throws PropertyVetoException {
         final EventListenerSupport<VetoableChangeListener> listenerSupport = 
EventListenerSupport.create(VetoableChangeListener.class);
         final List<VetoableChangeListener> calledListeners = new ArrayList<>();
-
         final VetoableChangeListener listener1 = 
createListener(calledListeners);
         final VetoableChangeListener listener2 = 
createListener(calledListeners);
         listenerSupport.addListener(listener1);
@@ -122,14 +122,12 @@ void testEventDispatchOrder() throws 
PropertyVetoException {
     @Test
     void testGetListeners() {
         final EventListenerSupport<VetoableChangeListener> listenerSupport = 
EventListenerSupport.create(VetoableChangeListener.class);
-
         final VetoableChangeListener[] listeners = 
listenerSupport.getListeners();
         assertEquals(0, listeners.length);
         assertEquals(VetoableChangeListener.class, 
listeners.getClass().getComponentType());
         final VetoableChangeListener[] empty = listeners;
-        //for fun, show that the same empty instance is used
+        // for fun, show that the same empty instance is used
         assertSame(empty, listenerSupport.getListeners());
-
         final VetoableChangeListener listener1 = 
EasyMock.createNiceMock(VetoableChangeListener.class);
         listenerSupport.addListener(listener1);
         assertEquals(1, listenerSupport.getListeners().length);
@@ -164,47 +162,42 @@ void testSerialization() throws IOException, 
ClassNotFoundException, PropertyVet
         final EventListenerSupport<VetoableChangeListener> listenerSupport = 
EventListenerSupport.create(VetoableChangeListener.class);
         listenerSupport.addListener(Function.identity()::apply);
         
listenerSupport.addListener(EasyMock.createNiceMock(VetoableChangeListener.class));
-
-        //serialize:
+        // serialize:
         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         try (ObjectOutputStream objectOutputStream = new 
ObjectOutputStream(outputStream)) {
             objectOutputStream.writeObject(listenerSupport);
         }
-
-        //deserialize:
+        // deserialize:
         @SuppressWarnings("unchecked")
-        final
-        EventListenerSupport<VetoableChangeListener> 
deserializedListenerSupport = (EventListenerSupport<VetoableChangeListener>) 
new ObjectInputStream(
+        final EventListenerSupport<VetoableChangeListener> 
deserializedListenerSupport = (EventListenerSupport<VetoableChangeListener>) 
new ObjectInputStream(
                 new 
ByteArrayInputStream(outputStream.toByteArray())).readObject();
-
-        //make sure we get a listener array back, of the correct component 
type, and that it contains only the serializable mock
+        // make sure we get a listener array back, of the correct component 
type, and that it contains only the serializable mock
         final VetoableChangeListener[] listeners = 
deserializedListenerSupport.getListeners();
         assertEquals(VetoableChangeListener.class, 
listeners.getClass().getComponentType());
         assertEquals(1, listeners.length);
-
-        //now verify that the mock still receives events; we can infer that 
the proxy was correctly reconstituted
+        // now verify that the mock still receives events; we can infer that 
the proxy was correctly reconstituted
         final VetoableChangeListener listener = listeners[0];
         final PropertyChangeEvent evt = new PropertyChangeEvent(new Date(), 
"Day", 7, 9);
         listener.vetoableChange(evt);
         EasyMock.replay(listener);
         deserializedListenerSupport.fire().vetoableChange(evt);
         EasyMock.verify(listener);
-
-        //remove listener and verify we get an empty array of listeners
+        // remove listener and verify we get an empty array of listeners
         deserializedListenerSupport.removeListener(listener);
         assertEquals(0, deserializedListenerSupport.getListeners().length);
     }
 
     @Test
     void testSubclassInvocationHandling() throws PropertyVetoException {
-
         final EventListenerSupport<VetoableChangeListener> 
eventListenerSupport = new EventListenerSupport<VetoableChangeListener>(
                 VetoableChangeListener.class) {
+
             private static final long serialVersionUID = 1L;
 
             @Override
             protected java.lang.reflect.InvocationHandler 
createInvocationHandler() {
                 return new ProxyInvocationHandler() {
+
                     @Override
                     public Object invoke(final Object proxy, final Method 
method, final Object[] args)
                             throws IllegalAccessException, 
IllegalArgumentException, InvocationTargetException {
@@ -214,7 +207,6 @@ public Object invoke(final Object proxy, final Method 
method, final Object[] arg
                 };
             }
         };
-
         final VetoableChangeListener listener = 
EasyMock.createNiceMock(VetoableChangeListener.class);
         eventListenerSupport.addListener(listener);
         final Object source = new Date();

Reply via email to