diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumClipboard.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumClipboard.java
index 176888dc736..df8626b5064 100644
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumClipboard.java
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumClipboard.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -245,10 +245,11 @@ public void flush() {
systemAssistant.flush();
}
- @Override public Object getContent(DataFormat dataFormat) {
+ @Override
+ public Object getContent(DataFormat dataFormat) {
if (dataCache != null) {
for (Pair pair : dataCache) {
- if (pair.getKey() == dataFormat) {
+ if (pair.getKey().equals(dataFormat)) {
return pair.getValue();
}
}
@@ -258,11 +259,11 @@ public void flush() {
ClipboardAssistance assistant =
(currentDragboard != null) ? currentDragboard : systemAssistant;
- if (dataFormat == DataFormat.IMAGE) {
+ if (DataFormat.IMAGE.equals(dataFormat)) {
return readImage();
- } else if (dataFormat == DataFormat.URL) {
+ } else if (DataFormat.URL.equals(dataFormat)) {
return assistant.getData(Clipboard.URI_TYPE);
- } else if (dataFormat == DataFormat.FILES) {
+ } else if (DataFormat.FILES.equals(dataFormat)) {
Object data = assistant.getData(Clipboard.FILE_LIST_TYPE);
if (data == null) return Collections.emptyList();
String[] paths = (String[]) data;
@@ -438,10 +439,11 @@ private boolean placeImage(final Image image) {
return set;
}
- @Override public boolean hasContent(DataFormat dataFormat) {
+ @Override
+ public boolean hasContent(DataFormat dataFormat) {
if (dataCache != null) {
for (Pair pair : dataCache) {
- if (pair.getKey() == dataFormat) {
+ if (pair.getKey().equals(dataFormat)) {
return true;
}
}
@@ -456,17 +458,17 @@ private boolean placeImage(final Image image) {
return false;
}
for (String t: stypes) {
- if (dataFormat == DataFormat.IMAGE &&
+ if (DataFormat.IMAGE.equals(dataFormat) &&
t.equalsIgnoreCase(Clipboard.RAW_IMAGE_TYPE)) {
return true;
- } else if (dataFormat == DataFormat.URL &&
+ } else if (DataFormat.URL.equals(dataFormat) &&
t.equalsIgnoreCase(Clipboard.URI_TYPE)) {
return true;
- } else if (dataFormat == DataFormat.IMAGE &&
+ } else if (DataFormat.IMAGE.equals(dataFormat) &&
t.equalsIgnoreCase(Clipboard.HTML_TYPE) &&
parseIMG(assistant.getData(Clipboard.HTML_TYPE)) != null) {
return true;
- } else if (dataFormat == DataFormat.FILES &&
+ } else if (DataFormat.FILES.equals(dataFormat) &&
t.equalsIgnoreCase(Clipboard.FILE_LIST_TYPE)) {
return true;
}
@@ -528,16 +530,16 @@ private boolean putContentToPeer(Pair... content) {
// might expect the JPG bits directly, rather than the DIB / TIFF bits.
// So what we do is, any IMAGE type DataFormat that comes in will be stored
// in DIB / TIFF, while specific bits will also be stored (in the future).
- if (dataFormat == DataFormat.IMAGE) {
+ if (DataFormat.IMAGE.equals(dataFormat)) {
dataSet = placeImage(convertObjectToImage(data));
- } else if (dataFormat == DataFormat.URL) {
+ } else if (DataFormat.URL.equals(dataFormat)) {
// TODO Weird, but this is how Glass wants it...
systemAssistant.setData(Clipboard.URI_TYPE, data);
dataSet = true;
- } else if (dataFormat == DataFormat.RTF) {
+ } else if (DataFormat.RTF.equals(dataFormat)) {
systemAssistant.setData(Clipboard.RTF_TYPE, data);
dataSet = true;
- } else if (dataFormat == DataFormat.FILES) {
+ } else if (DataFormat.FILES.equals(dataFormat)) {
// Have to convert from List to String[]
List list = (List)data;
if (list.size() != 0) {
@@ -551,8 +553,10 @@ private boolean putContentToPeer(Pair... content) {
}
} else {
if (data instanceof Serializable) {
- if ((dataFormat != DataFormat.PLAIN_TEXT && dataFormat != DataFormat.HTML) ||
- !(data instanceof String))
+ if (
+ (!(DataFormat.PLAIN_TEXT.equals(dataFormat)) && !(DataFormat.HTML.equals(dataFormat))) ||
+ !(data instanceof String)
+ )
{
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/input/DataFormat.java b/modules/javafx.graphics/src/main/java/javafx/scene/input/DataFormat.java
index d8107ca13f5..8f960e2c569 100644
--- a/modules/javafx.graphics/src/main/java/javafx/scene/input/DataFormat.java
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/input/DataFormat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,13 +25,9 @@
package javafx.scene.input;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
-
-import com.sun.javafx.util.WeakReferenceQueue;
import javafx.beans.NamedArg;
/**
@@ -41,14 +37,9 @@
*/
public class DataFormat {
- /**
- * A static cache of all DataFormats created and currently in use. This is needed
- * by the underlying implementation, such that, given a mime type, we can determine
- * the associated DataFormat. The OS level is going to supply us with a mime type
- * (or other string based key), and we need to be able to map this back to the FX
- * DataFormat.
- */
- private static final WeakReferenceQueue DATA_FORMAT_LIST = new WeakReferenceQueue<>();
+ // A static registry of DataFormats for the purposes of checking against constructing DataFormats
+ // that contain mismatched mime types.
+ private static final HashMap registry = new HashMap<>();
/**
* Represents a plain text string.
@@ -101,7 +92,7 @@ public class DataFormat {
* A set of identifiers, typically mime types, for this DataFormat.
* In most cases this will be a single String.
*/
- private final Set identifier;
+ private final Set identifiers;
/**
* Create a new DataFormat, specifying the set of ids that are associated with
@@ -126,25 +117,37 @@ public class DataFormat {
* to drag data of this type from/to {@link javafx.embed.swing.JFXPanel}.
*
* @param ids The set of ids used to represent this DataFormat on the clipboard.
- * @throws IllegalArgumentException if one of the given mime types is already
- * assigned to another DataFormat.
+ * @throws IllegalArgumentException if one of the given ids is already
+ * assigned to another DataFormat with a different set of ids
+ * @throws NullPointerException if any of the ids is null
*/
public DataFormat(@NamedArg("ids") String... ids) {
- DATA_FORMAT_LIST.cleanup();
- if (ids != null) {
- for (String id : ids) {
- if (lookupMimeType(id) != null) {
- throw new IllegalArgumentException("DataFormat '" + id +
- "' already exists.");
+ if ((ids == null) || (ids.length == 0)) {
+ identifiers = Set.of();
+ return;
+ }
+
+ identifiers = Set.of(ids);
+ // check for mismatched formats
+ boolean isNew = true;
+ synchronized (registry) {
+ for (String id : identifiers) {
+ DataFormat f = registry.get(id);
+ if (f != null) {
+ if (!identifiers.equals(f.identifiers)) {
+ throw new IllegalArgumentException("DataFormat '" + id + "' already exists.");
+ }
+ isNew = false;
}
}
- this.identifier = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(ids)));
- } else {
- this.identifier = Collections.emptySet();
- }
- // Add to the statis data format list.
- DATA_FORMAT_LIST.add(this);
+ // add to the registry if new
+ if (isNew) {
+ for (String id : ids) {
+ registry.put(id, this);
+ }
+ }
+ }
}
/**
@@ -152,7 +155,7 @@ public DataFormat(@NamedArg("ids") String... ids) {
* @return an unmodifiable set that is never null.
*/
public final Set getIdentifiers() {
- return identifier;
+ return identifiers;
}
/**
@@ -160,15 +163,15 @@ public final Set getIdentifiers() {
* @return a string representation of this {@code DataFormat} object.
*/
@Override public String toString() {
- if (identifier.isEmpty()) {
+ if (identifiers.isEmpty()) {
return "[]";
- } else if (identifier.size() == 1) {
+ } else if (identifiers.size() == 1) {
StringBuilder sb = new StringBuilder("[");
- sb.append(identifier.iterator().next());
+ sb.append(identifiers.iterator().next());
return (sb.append("]").toString());
} else {
StringBuilder b = new StringBuilder("[");
- Iterator itr = identifier.iterator();
+ Iterator itr = identifiers.iterator();
while (itr.hasNext()) {
b = b.append(itr.next());
if (itr.hasNext()) {
@@ -187,7 +190,7 @@ public final Set getIdentifiers() {
@Override public int hashCode() {
int hash = 7;
- for (String id : identifier) {
+ for (String id : identifiers) {
hash = 31 * hash + id.hashCode();
}
@@ -199,18 +202,12 @@ public final Set getIdentifiers() {
* @param obj the reference object with which to compare.
* @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
*/
- @Override public boolean equals(Object obj) {
- if (obj == null || ! (obj instanceof DataFormat)) {
- return false;
- }
-
- DataFormat otherDataFormat = (DataFormat) obj;
-
- if (identifier.equals(otherDataFormat.identifier)) {
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
return true;
}
-
- return false;
+ return (obj instanceof DataFormat f) && identifiers.equals(f.identifiers);
}
/**
@@ -224,13 +221,8 @@ public static DataFormat lookupMimeType(String mimeType) {
return null;
}
- Iterator itr = DATA_FORMAT_LIST.iterator();
- while (itr.hasNext()) {
- DataFormat dataFormat = (DataFormat) itr.next();
- if (dataFormat.getIdentifiers().contains(mimeType)) {
- return dataFormat;
- }
+ synchronized (registry) {
+ return registry.get(mimeType);
}
- return null;
}
}
diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/DataFormatTest.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/DataFormatTest.java
index 6d1fd0fc7f9..f600e814c58 100644
--- a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/DataFormatTest.java
+++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/DataFormatTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,18 +25,18 @@
package test.javafx.scene.input;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.stream.Stream;
import javafx.scene.input.DataFormat;
-
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
public class DataFormatTest {
@@ -69,9 +69,9 @@ public void testMimeTypes(DataFormat format, String mime1, String mime2) {
@ParameterizedTest
@MethodSource("getParams")
public void dataFormatsShouldBeFound(DataFormat format, String mime1, String mime2) {
- assertSame(format, DataFormat.lookupMimeType(mime1));
+ assertEquals(format, DataFormat.lookupMimeType(mime1));
if (mime2 != null) {
- assertSame(format, DataFormat.lookupMimeType(mime2));
+ assertEquals(format, DataFormat.lookupMimeType(mime2));
}
}
@@ -84,14 +84,30 @@ public void testToString(DataFormat format, String mime1, String mime2) {
@ParameterizedTest
@MethodSource("getParams")
- public void shouldNotBePossibleToReuseMimeTypes(DataFormat format, String mime1, String mime2) {
+ public void shouldBePossibleToReuseEquivalentMimeTypes(DataFormat f, String mime1, String mime2) {
+ DataFormat f1 = new DataFormat(f.getIdentifiers().toArray(String[]::new));
+ assertEquals(f, f1);
+ DataFormat f2 = new DataFormat(f.getIdentifiers().toArray(String[]::new));
+ assertEquals(f, f2);
+ assertEquals(f1, f2);
+ }
+
+ @Test
+ public void shouldNotBePossibleToRegisterMismatchedFormats() {
+ // using DataFormat.FILES
+ assertThrows(IllegalArgumentException.class, () -> {
+ new DataFormat("application/x-java-file-list");
+ });
assertThrows(IllegalArgumentException.class, () -> {
- DataFormat customEqual = new DataFormat(format.getIdentifiers().toArray(
- new String[format.getIdentifiers().size()]));
+ new DataFormat("java.file-list");
+ });
+ // using custom
+ DataFormat f1 = new DataFormat("test/foo", "test/bar");
+ assertThrows(IllegalArgumentException.class, () -> {
+ new DataFormat("test/foo");
});
}
-
@ParameterizedTest
@MethodSource("getParams")
public void testEqualsAndHashCode(DataFormat format, String mime1, String mime2) {
@@ -101,4 +117,20 @@ public void testEqualsAndHashCode(DataFormat format, String mime1, String mime2)
assertFalse(uniqueFormat.equals(format));
assertFalse(uniqueFormat.hashCode() == format.hashCode());
}
+
+ @Test
+ public void noMoreNullMimeTypes() {
+ String mime = null;
+ assertThrows(NullPointerException.class, () -> {
+ new DataFormat(mime);
+ });
+ }
+
+ @Test
+ public void nullArrayIsAllowedForCompatibilityReasons() {
+ String[] mimes = null;
+ assertDoesNotThrow(() -> {
+ new DataFormat(mimes);
+ });
+ }
}