From d24c482a8a3b5f8fb75993cee6167da5b9ec6ecf Mon Sep 17 00:00:00 2001 From: Basil Peace Date: Thu, 27 Jan 2022 01:48:16 +0300 Subject: [PATCH 1/4] Annotate Java Beans property listeners for nullness --- .../classes/java/beans/ChangeListenerMap.java | 18 +++++++------ .../beans/IndexedPropertyChangeEvent.java | 6 ++++- .../java/beans/PropertyChangeEvent.java | 26 +++++++++++-------- .../java/beans/PropertyChangeSupport.java | 8 +++--- .../java/beans/VetoableChangeSupport.java | 25 +++++++++--------- 5 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java index 00c8ccdd5fcf7..7b5dfebeb69ed 100644 --- a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java +++ b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java @@ -25,6 +25,8 @@ package java.beans; import org.checkerframework.checker.interning.qual.UsesObjectEquals; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.framework.qual.AnnotatedFor; import java.util.ArrayList; @@ -47,9 +49,9 @@ * * @author Sergey A. Malenkov */ -@AnnotatedFor({"interning"}) +@AnnotatedFor({"interning", "nullable"}) abstract @UsesObjectEquals class ChangeListenerMap { - private Map map; + private @MonotonicNonNull Map<@Nullable String, L[]> map; /** * Creates an array of listeners. @@ -78,7 +80,7 @@ * @param name the name of the property to listen on * @param listener the listener to process events */ - public final synchronized void add(String name, L listener) { + public final synchronized void add(@Nullable String name, L listener) { if (this.map == null) { this.map = new HashMap<>(); } @@ -103,7 +105,7 @@ public final synchronized void add(String name, L listener) { * @param name the name of the property to listen on * @param listener the listener to process events */ - public final synchronized void remove(String name, L listener) { + public final synchronized void remove(@Nullable String name, L listener) { if (this.map != null) { L[] array = this.map.get(name); if (array != null) { @@ -135,7 +137,7 @@ public final synchronized void remove(String name, L listener) { * @param name the name of the property * @return the corresponding list of listeners */ - public final synchronized L[] get(String name) { + public final synchronized L[] get(@Nullable String name) { return (this.map != null) ? this.map.get(name) : null; @@ -147,7 +149,7 @@ public final synchronized L[] get(String name) { * @param name the name of the property * @param listeners new list of listeners */ - public final void set(String name, L[] listeners) { + public final void set(@Nullable String name, @Nullable L[] listeners) { if (listeners != null) { if (this.map == null) { this.map = new HashMap<>(); @@ -196,7 +198,7 @@ public final synchronized L[] getListeners() { * @param name the name of the property * @return an array of listeners for the named property */ - public final L[] getListeners(String name) { + public final L[] getListeners(@Nullable String name) { if (name != null) { L[] listeners = get(name); if (listeners != null) { @@ -214,7 +216,7 @@ public final L[] getListeners(String name) { * @return {@code true} if at least one listener exists or * {@code false} otherwise */ - public final synchronized boolean hasListeners(String name) { + public final synchronized boolean hasListeners(@Nullable String name) { if (this.map == null) { return false; } diff --git a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java index 40d4f9d7cd56d..909beb3a0c4da 100644 --- a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java +++ b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java @@ -24,6 +24,9 @@ */ package java.beans; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.AnnotatedFor; + /** * An "IndexedPropertyChange" event gets delivered whenever a component that * conforms to the JavaBeans™ specification (a "bean") changes a bound @@ -40,6 +43,7 @@ * @since 1.5 * @author Mark Davidson */ +@AnnotatedFor({"nullable"}) public class IndexedPropertyChangeEvent extends PropertyChangeEvent { private static final long serialVersionUID = -320227448495806870L; @@ -56,7 +60,7 @@ public class IndexedPropertyChangeEvent extends PropertyChangeEvent { * @param index index of the property element that was changed. */ public IndexedPropertyChangeEvent(Object source, String propertyName, - Object oldValue, Object newValue, + @Nullable Object oldValue, @Nullable Object newValue, int index) { super (source, propertyName, oldValue, newValue); this.index = index; diff --git a/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java b/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java index ec7d8ed51c093..a4e76eb235a41 100644 --- a/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java +++ b/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java @@ -27,6 +27,9 @@ import java.util.EventObject; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.AnnotatedFor; + /** * A "PropertyChange" event gets delivered whenever a bean changes a "bound" * or "constrained" property. A PropertyChangeEvent object is sent as an @@ -46,6 +49,7 @@ * * @since 1.1 */ +@AnnotatedFor({"nullable"}) public class PropertyChangeEvent extends EventObject { private static final long serialVersionUID = 7042693688939648123L; @@ -59,8 +63,8 @@ public class PropertyChangeEvent extends EventObject { * * @throws IllegalArgumentException if {@code source} is {@code null} */ - public PropertyChangeEvent(Object source, String propertyName, - Object oldValue, Object newValue) { + public PropertyChangeEvent(Object source, @Nullable String propertyName, + @Nullable Object oldValue, @Nullable Object newValue) { super(source); this.propertyName = propertyName; this.newValue = newValue; @@ -73,7 +77,7 @@ public PropertyChangeEvent(Object source, String propertyName, * @return The programmatic name of the property that was changed. * May be null if multiple properties have changed. */ - public String getPropertyName() { + public @Nullable String getPropertyName() { return propertyName; } @@ -83,7 +87,7 @@ public String getPropertyName() { * @return The new value for the property, expressed as an Object. * May be null if multiple properties have changed. */ - public Object getNewValue() { + public @Nullable Object getNewValue() { return newValue; } @@ -93,7 +97,7 @@ public Object getNewValue() { * @return The old value for the property, expressed as an Object. * May be null if multiple properties have changed. */ - public Object getOldValue() { + public @Nullable Object getOldValue() { return oldValue; } @@ -102,7 +106,7 @@ public Object getOldValue() { * * @param propagationId The propagationId object for the event. */ - public void setPropagationId(Object propagationId) { + public void setPropagationId(@Nullable Object propagationId) { this.propagationId = propagationId; } @@ -116,7 +120,7 @@ public void setPropagationId(Object propagationId) { * @return the propagationId object associated with a bound/constrained * property update. */ - public Object getPropagationId() { + public @Nullable Object getPropagationId() { return propagationId; } @@ -124,26 +128,26 @@ public Object getPropagationId() { * name of the property that changed. May be null, if not known. * @serial */ - private String propertyName; + private @Nullable String propertyName; /** * New value for property. May be null if not known. * @serial */ - private Object newValue; + private @Nullable Object newValue; /** * Previous value for property. May be null if not known. * @serial */ - private Object oldValue; + private @Nullable Object oldValue; /** * Propagation ID. May be null. * @serial * @see #getPropagationId */ - private Object propagationId; + private @Nullable Object propagationId; /** * Returns a string representation of the object. diff --git a/src/java.desktop/share/classes/java/beans/PropertyChangeSupport.java b/src/java.desktop/share/classes/java/beans/PropertyChangeSupport.java index 33b595e5dc2c8..6972116020bd8 100644 --- a/src/java.desktop/share/classes/java/beans/PropertyChangeSupport.java +++ b/src/java.desktop/share/classes/java/beans/PropertyChangeSupport.java @@ -263,7 +263,7 @@ public void addPropertyChangeListener( * returned. * @since 1.4 */ - @PolyUIEffect public @PolyUI PropertyChangeListener[] getPropertyChangeListeners(@PolyUI PropertyChangeSupport this, String propertyName) { + @PolyUIEffect public @PolyUI PropertyChangeListener[] getPropertyChangeListeners(@PolyUI PropertyChangeSupport this, @Nullable String propertyName) { return this.map.getListeners(propertyName); } @@ -281,7 +281,7 @@ public void addPropertyChangeListener( * @param oldValue the old value of the property * @param newValue the new value of the property */ - @PolyUIEffect public void firePropertyChange(@PolyUI PropertyChangeSupport this, String propertyName, @Nullable @FenumTop Object oldValue, @Nullable @FenumTop Object newValue) { + @PolyUIEffect public void firePropertyChange(@PolyUI PropertyChangeSupport this, @Nullable String propertyName, @Nullable @FenumTop Object oldValue, @Nullable @FenumTop Object newValue) { if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { firePropertyChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); } @@ -302,7 +302,7 @@ public void addPropertyChangeListener( * @param newValue the new value of the property * @since 1.2 */ - @PolyUIEffect public void firePropertyChange(@PolyUI PropertyChangeSupport this, String propertyName, @FenumTop int oldValue, @FenumTop int newValue) { + @PolyUIEffect public void firePropertyChange(@PolyUI PropertyChangeSupport this, @Nullable String propertyName, @FenumTop int oldValue, @FenumTop int newValue) { if (oldValue != newValue) { firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } @@ -323,7 +323,7 @@ public void addPropertyChangeListener( * @param newValue the new value of the property * @since 1.2 */ - @PolyUIEffect public void firePropertyChange(@PolyUI PropertyChangeSupport this, String propertyName, boolean oldValue, boolean newValue) { + @PolyUIEffect public void firePropertyChange(@PolyUI PropertyChangeSupport this, @Nullable String propertyName, boolean oldValue, boolean newValue) { if (oldValue != newValue) { firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } diff --git a/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java b/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java index 8b0fd35d6231c..5e7073a177ec4 100644 --- a/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java +++ b/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java @@ -25,6 +25,7 @@ package java.beans; import org.checkerframework.checker.interning.qual.UsesObjectEquals; +import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.framework.qual.AnnotatedFor; import java.io.Serializable; @@ -82,7 +83,7 @@ * @see PropertyChangeSupport * @since 1.1 */ -@AnnotatedFor({"interning"}) +@AnnotatedFor({"interning", "nullable"}) public @UsesObjectEquals class VetoableChangeSupport implements Serializable { private VetoableChangeListenerMap map = new VetoableChangeListenerMap(); @@ -108,7 +109,7 @@ public VetoableChangeSupport(Object sourceBean) { * * @param listener The VetoableChangeListener to be added */ - public void addVetoableChangeListener(VetoableChangeListener listener) { + public void addVetoableChangeListener(@Nullable VetoableChangeListener listener) { if (listener == null) { return; } @@ -134,7 +135,7 @@ public void addVetoableChangeListener(VetoableChangeListener listener) { * * @param listener The VetoableChangeListener to be removed */ - public void removeVetoableChangeListener(VetoableChangeListener listener) { + public void removeVetoableChangeListener(@Nullable VetoableChangeListener listener) { if (listener == null) { return; } @@ -199,8 +200,8 @@ public VetoableChangeListener[] getVetoableChangeListeners(){ * @since 1.2 */ public void addVetoableChangeListener( - String propertyName, - VetoableChangeListener listener) { + @Nullable String propertyName, + @Nullable VetoableChangeListener listener) { if (listener == null || propertyName == null) { return; } @@ -225,8 +226,8 @@ public void addVetoableChangeListener( * @since 1.2 */ public void removeVetoableChangeListener( - String propertyName, - VetoableChangeListener listener) { + @Nullable String propertyName, + @Nullable VetoableChangeListener listener) { if (listener == null || propertyName == null) { return; } @@ -247,7 +248,7 @@ public void removeVetoableChangeListener( * returned. * @since 1.4 */ - public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) { + public VetoableChangeListener[] getVetoableChangeListeners(@Nullable String propertyName) { return this.map.getListeners(propertyName); } @@ -272,7 +273,7 @@ public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) * @param newValue the new value of the property * @throws PropertyVetoException if one of listeners vetoes the property update */ - public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) + public void fireVetoableChange(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue) throws PropertyVetoException { if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { fireVetoableChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); @@ -301,7 +302,7 @@ public void fireVetoableChange(String propertyName, Object oldValue, Object newV * @throws PropertyVetoException if one of listeners vetoes the property update * @since 1.2 */ - public void fireVetoableChange(String propertyName, int oldValue, int newValue) + public void fireVetoableChange(@Nullable String propertyName, int oldValue, int newValue) throws PropertyVetoException { if (oldValue != newValue) { fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); @@ -330,7 +331,7 @@ public void fireVetoableChange(String propertyName, int oldValue, int newValue) * @throws PropertyVetoException if one of listeners vetoes the property update * @since 1.2 */ - public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue) + public void fireVetoableChange(@Nullable String propertyName, boolean oldValue, boolean newValue) throws PropertyVetoException { if (oldValue != newValue) { fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); @@ -411,7 +412,7 @@ else if (named == null) { * @return true if there are one or more listeners for the given property * @since 1.2 */ - public boolean hasListeners(String propertyName) { + public boolean hasListeners(@Nullable String propertyName) { return this.map.hasListeners(propertyName); } From 687339b50d25d8e5f60a4959394a992807efd3eb Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 29 Jan 2022 09:02:48 -0800 Subject: [PATCH 2/4] Fix `@AnnotatedFor` arguments --- .../share/classes/java/beans/ChangeListenerMap.java | 2 +- .../share/classes/java/beans/IndexedPropertyChangeEvent.java | 2 +- .../share/classes/java/beans/PropertyChangeEvent.java | 2 +- .../share/classes/java/beans/VetoableChangeSupport.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java index 7b5dfebeb69ed..b868e2c08f994 100644 --- a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java +++ b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java @@ -49,7 +49,7 @@ * * @author Sergey A. Malenkov */ -@AnnotatedFor({"interning", "nullable"}) +@AnnotatedFor({"interning", "nullness"}) abstract @UsesObjectEquals class ChangeListenerMap { private @MonotonicNonNull Map<@Nullable String, L[]> map; diff --git a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java index 909beb3a0c4da..843a848404094 100644 --- a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java +++ b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java @@ -43,7 +43,7 @@ * @since 1.5 * @author Mark Davidson */ -@AnnotatedFor({"nullable"}) +@AnnotatedFor({"nullness"}) public class IndexedPropertyChangeEvent extends PropertyChangeEvent { private static final long serialVersionUID = -320227448495806870L; diff --git a/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java b/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java index a4e76eb235a41..bbd05ecd317ce 100644 --- a/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java +++ b/src/java.desktop/share/classes/java/beans/PropertyChangeEvent.java @@ -49,7 +49,7 @@ * * @since 1.1 */ -@AnnotatedFor({"nullable"}) +@AnnotatedFor({"nullness"}) public class PropertyChangeEvent extends EventObject { private static final long serialVersionUID = 7042693688939648123L; diff --git a/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java b/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java index 5e7073a177ec4..057d3882c7197 100644 --- a/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java +++ b/src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java @@ -83,7 +83,7 @@ * @see PropertyChangeSupport * @since 1.1 */ -@AnnotatedFor({"interning", "nullable"}) +@AnnotatedFor({"interning", "nullness"}) public @UsesObjectEquals class VetoableChangeSupport implements Serializable { private VetoableChangeListenerMap map = new VetoableChangeListenerMap(); From 651eb4fa350a8db419b1c0a6385b923d2955dd23 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 29 Jan 2022 09:35:43 -0800 Subject: [PATCH 3/4] Add comments --- .../share/classes/java/beans/ChangeListenerMap.java | 1 + .../share/classes/java/beans/IndexedPropertyChangeEvent.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java index b868e2c08f994..f2be229a30a44 100644 --- a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java +++ b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java @@ -49,6 +49,7 @@ * * @author Sergey A. Malenkov */ +@CFComment("nullness: null values are permitted for property names, though not documented in Javadoc") @AnnotatedFor({"interning", "nullness"}) abstract @UsesObjectEquals class ChangeListenerMap { private @MonotonicNonNull Map<@Nullable String, L[]> map; diff --git a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java index 843a848404094..a3e30c2cf0afe 100644 --- a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java +++ b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java @@ -44,6 +44,8 @@ * @author Mark Davidson */ @AnnotatedFor({"nullness"}) +@CFComment({"nullness: don't permit null property name, class documentation may ", + "be a typo caused by cut-and-paste from PropertyChangeEvent"}) public class IndexedPropertyChangeEvent extends PropertyChangeEvent { private static final long serialVersionUID = -320227448495806870L; From 130f790894229ecc55f1169a04d9dc2b9340e71b Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Thu, 3 Feb 2022 10:28:40 -0800 Subject: [PATCH 4/4] Add import statements --- src/java.desktop/share/classes/java/beans/ChangeListenerMap.java | 1 + .../share/classes/java/beans/IndexedPropertyChangeEvent.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java index f2be229a30a44..ef05069982426 100644 --- a/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java +++ b/src/java.desktop/share/classes/java/beans/ChangeListenerMap.java @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.framework.qual.AnnotatedFor; +import org.checkerframework.framework.qual.CFComment; import java.util.ArrayList; import java.util.Collections; diff --git a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java index a3e30c2cf0afe..82efeda9e7536 100644 --- a/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java +++ b/src/java.desktop/share/classes/java/beans/IndexedPropertyChangeEvent.java @@ -26,6 +26,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.framework.qual.AnnotatedFor; +import org.checkerframework.framework.qual.CFComment; /** * An "IndexedPropertyChange" event gets delivered whenever a component that