-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8159055: ImageIcon.setImage can't handle null parameter #25767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
👋 Welcome back psadhukhan! A progress list of the required criteria for merging this PR into |
@prsadhuk This change is no longer ready for integration - check the PR body for details. |
Webrevs
|
Does it require to update the doc as well to explicitly mention about NULL parameter ? |
I feel it's better to inform the user in the spec otherwise application may not know what will be done with NULL image |
CSR needed ? |
yes..i will raise it once we are done with this PR approval.. |
Ok... |
No, as I mentioned in the description the MediaTracker creation and id addition, tracking and removal is supposedly not required for null image. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
Raised CSR https://bugs.openjdk.org/browse/JDK-8359398 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ImageIcon
constructor that accepts Image
has the same problem, or a similar problem.
jdk/src/java.desktop/share/classes/javax/swing/ImageIcon.java
Lines 224 to 226 in 9d06057
public ImageIcon (Image image) { | |
this.image = image; | |
Object o = image.getProperty("comment", imageObserver); |
It throws NullPointerException
if a null
parameter is passed. Other constructors handle null
values, so you should unify handling of null
images.
@@ -367,10 +371,14 @@ public Image getImage() { | |||
|
|||
/** | |||
* Sets the image displayed by this icon. | |||
* Setting a {@code null} image will not render any image icon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Setting a {@code null} image will not render any image icon. | |
* Setting a {@code null} image renders no icon. |
I think this is better: it's shorter, it uses the present tense because it's fact and it doesn't depend on anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the existing text was OK. And "will" is better because it hasn't happened yet.
Rendering will happen later. Not when you construct this object.
My personally preferred wording would be something like
Setting a {@code null} image means any existing image will be removed and no image will be rendered.
Now .. what should happen to an existing description when you set a new image ?
Apps can call setDescription() but in what order ?
The only thing I can say here is that I think if the image is null we probably should clear the description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have modified the wording as per suggestion and also clearing the description now if image is null..
@@ -367,10 +371,14 @@ public Image getImage() { | |||
|
|||
/** | |||
* Sets the image displayed by this icon. | |||
* Setting a {@code null} image will not render any image icon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the existing text was OK. And "will" is better because it hasn't happened yet.
Rendering will happen later. Not when you construct this object.
My personally preferred wording would be something like
Setting a {@code null} image means any existing image will be removed and no image will be rendered.
Now .. what should happen to an existing description when you set a new image ?
Apps can call setDescription() but in what order ?
The only thing I can say here is that I think if the image is null we probably should clear the description.
@@ -223,6 +224,9 @@ public ImageIcon(Image image, String description) { | |||
*/ | |||
public ImageIcon (Image image) { | |||
this.image = image; | |||
if (image == null) { | |||
return; | |||
} | |||
Object o = image.getProperty("comment", imageObserver); | |||
if (o instanceof String) { | |||
description = (String) o; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. This is wasted work if the app calls ImageIcon(Image, String) because that promptly over-writes whatever was obtained via this code.
Also that other constructor (which github won't let me comment on) installs the description even if the image is null.
That seems to be inconsistent with everything else that has a null description for a null image.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also that other constructor (which github won't let me comment on) installs the description even if the image is null.
I don't see it as a problem.
If the app developer wants to initialise the object with null
image and a description, why don't we let them do it?
The image may be auto-generated and while it's generated, the image in ImageIcon
remains null
; when the image is read the app calls ImageIcon.setImage
to the set now available image and expects the description being preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now .. what should happen to an existing description when you set a new image ?
Apps can call setDescription() but in what order ?
The only thing I can say here is that I think if the image is null we probably should clear the description.
Nothing should happen to the description. These are two fields which can be changed independently now, why would we enforce an order on method calls?
Changing the image shouldn't change the description; similarly, changing description doesn't change the image.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also that other constructor (which github won't let me comment on) installs the description even if the image is null.
I don't see it as a problem.
If the app developer wants to initialise the object with
null
image and a description, why don't we let them do it?
Because that would be inconsistent with every other constructor.
See
ImagIcon(String filename)
ImageIcon(String filename, String description)
ImageIcon(String URL)
ImageIcon(String URL, String description)
ImageIcon(byte[] imageData)
ImageIcon(byte[] imageData, String description)
None of these set the description if the image resolves to null.
Also clearly
ImageIcon(Image image)
and
ImageIcon(Image image, String description)
where image == null can't be doing it today
because a NPE is thrown before it gets there.
So we can have that case do whatever we want if we allow a null image.
And consistency seems the best choice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And consistency seems the best choice.
Then our best choice is not to touch ImageIcon(Image)
and leave it to throw NullPointerException
.
All the constructors except for ImageIcon((String) null)
throw NullPointerException
if the first argument is null
.
Strangely enough, ImageIcon((String) null)
creates a non-null
image.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we leave it to throw NPE, then JCK might crib, if at some point they decide to add test to test null image and since javadoc doesn't mention it can throw NPE, it will result in non-compliance and then we have to modify javadoc to clarify it, so I guess it's best to take preventive action now..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we leave it to throw NPE, then JCK might crib, if at some point they decide to add test to test null image and since javadoc doesn't mention it can throw NPE, it will result in non-compliance and then we have to modify javadoc to clarify it, so I guess it's best to take preventive action now..
But it is consistent with the current behaviour and it is not specified at the moment.
If we explicitly specify the constructors throw NPE if a null
parameter is passed, then JCK will add a test that confirms the specified behaviour. All the constructors come into picture then, which seems out of scope for this issue…
If we don't change the behaviour of constructors and JCK challenges the implementation, then such a JCK test has no foundation in the API specification; yet it also leads to JCK challenge of the clarity of the specification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not needed as the initial value of description
is null
:
/reviewers 2 reviewer |
@aivanov-jdk |
I have removed resetting "description" from constructor as it is already null there..Retained in setImage and reset width/height also.. |
if (image == null) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, are we changing the constructors that accept Image
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we changing the constructor to accept null
as the Image
parameter?
There's a long thread about consistent behaviour of the ImageIcon
constructors, yet no decision has been taken.
@prrace mentioned consistency among constructors, and ImageIcon
constructors consistently throw NullPointerException
for null
image. In fact, he referred to setting the description
field, but this is moot if a constructor throws.
Object o = image.getProperty("comment", imageObserver); | ||
if (o instanceof String) { | ||
description = (String) o; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To address Phil's comment:
This is wasted work if the app calls ImageIcon(Image, String) because that promptly over-writes whatever was obtained via this code.
If we're going to change the constructors, to avoid this wasted work when ImageIcon(Image, String)
constructor is called, I suggest moving the work into ImageIcon(Image, String)
and implement ImageIcon
like this:
public ImageIcon (Image image) {
String description = null;
if (image != null) {
Object o = image.getProperty("comment", null);
if (o instanceof String) {
description = (String) o;
}
}
this(image, description);
It is allowed in JDK 22 and later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this was a preview language feature until JDK 25.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't done my due diligence to verify whether this feature can be used. Even if this particular way can't be used, it's still a viable option, just move the code above into a helper method.
public ImageIcon(Image image, String description) {
this.image = image;
this.description = description;
loadImage(image);
}
public ImageIcon (Image image) {
this(image, getImageComment(image));
}
/**
* {@return the {@code "comment"} property of the image
* if the value of the property is a sting}
* @param image the image to get the {@code "comment"} property
*/
private static String getImageComment(Image image) {
if (image == null) {
return null;
}
Object o = image.getProperty("comment", null);
return (o instanceof String) ? (String) o : null;
}
This should be done separately from this changeset, I think, to have shorter, more specific changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this was a preview language feature until JDK 25.
Huh… We're developing JDK 26 now, so the Flexible Constructor Bodies [JEP 513] feature can safely be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it complicates backports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it complicates backports.
Yes, it complicates backports, but this implies backports are possible. If the specification is updated, the backports will likely be impossible.
I guess resetting the description came from the fact that description is mentioned as "Sets the description of the image. This is meant to be a brief textual description of the object." so if image is null, the description of the image logically should be nothing. But again, as pointed out, description has its own setter and getter so user would expect whatever description is stored either by ImageIcon(Image image, String description) constructor or |
I agree that a description for a Then, it also depends on how it's really handled. If the image in the
Exactly. Updating the specification for I prefer leaving the description intact.
Have anyone submit a bug to address this deficiency? If not, I'll submit one. |
I think we need to have the "whole picture" tested to make sure everything does as we expect. |
I tested with null image/imagedata and invalid imagedata and it seems only these constructors throw NPE (current JDK) for null image (invalid image doesnt throw any exception) ImageIcon(image, desc) Current PR checks for null in ImageIcon(image) so it will not throw NPE but ImageIcon(byte[]) will still throw.. |
import java.awt.Image; | ||
import javax.swing.ImageIcon; | ||
|
||
public class ImageIconNullImageTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we're going to update the test later with follow-up fixes…
However, I suggest dropping ImageIcon-
from the test class name — the test is in ImageIcon
folder, therefore it tests ImageIcon
.
I wrote a JUnit test, it's more convenient than a jtreg test:
|
I submitted JDK-8361610: Avoid wasted work in ImageIcon(Image) for setting description for updating |
You told me (off-line) that null URL also throws NPE and I see that too. This class is a bit of a mess of accidental behaviours and scant documentation. Let's document all the NPE behaviours and include a test that verifies it. And invalid data like "new byte[0]" or null for a file name, or pointing to something that isn't an image file, or an invalid URL .. etc .. results in an Image instance being present, but it will never be completed so can never be drawn. I think we need a class level statement like We probably should add the similar text on setImage(). We can also test the invalid image data cases too. |
Documented all NPE behavour and added class-level statement. |
When trying to call 'icon.setImage(null);' where 'icon' is an instance of ImageIcon, a null pointer exception is thrown at runtime.
The code tried to get the
id
for that image and instantiatesMediaTracker
to associate the null image to thatid
and checks the status of loading this null image, removes the null image from the tracker and then tries to get the image width where it throws NPE as image is null.It's better to not go through all MediaTracker usage and bail out initially itself for null image..
Progress
Issues
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/25767/head:pull/25767
$ git checkout pull/25767
Update a local copy of the PR:
$ git checkout pull/25767
$ git pull https://git.openjdk.org/jdk.git pull/25767/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 25767
View PR using the GUI difftool:
$ git pr show -t 25767
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/25767.diff
Using Webrev
Link to Webrev Comment