### Summary

This PR makes the `DataFormat` constructor private:

private DataFormat(@NamedArg("ids") String... ids)


and replaces it with

public static DataFormat of(String ... ids)


### Problem

There seems to be several issues with DataFormat API and implementation 
discovered during a Clipboard-related code review:

1. `static DataFormat::lookupMimeType(String)` is not thread safe: while 
iterating over previously registered entries in the `DATA_FORMAT_LIST` another 
thread might create a new instance (DataFormat L227)

2. `public DataFormat(String...)` constructor might throw an 
`IllegalArgumentException` if one of the given mime types is already assigned 
to another `DataFormat`. The origin of this requirement is unclear, but one 
possible issue I can see is if the application has two libraries that both 
attempt to create a `DataFormat` for let's say `"text/css"`. Then, depending on 
the timing or the exact code path, an exception will be thrown for which the 
library(-ies) might not be prepared. The constructor is also not thread safe.

3. To avoid a situation mentioned in bullet 2, a developer would is typically 
call `lookupMimeType()` to obtain an already registered instance, followed by a 
constructor call if such an instance has not been found. An example of such 
code can be seen in webkit/UIClientImpl:299 - but even then, despite that 
two-step process being synchronized, the code might still fail if *some other* 
library or the application attempts to create a new instance of DataFormat, 
since the constructor itself is not synchronized.

4. `DataFormat(new String[] { null })` is allowed but makes no sense!

Why do we need to have the registry of previously created instances? Unclear. 
My theory is that the DataFormat allows to have multiple mime-types (ids) - 
example being `DataFormat.FILES = new 
DataFormat("application/x-java-file-list", "java.file-list");` - and the 
registry was added to prevent creation of a `DataFormat` with just one id for 
some reason.

What should be done?
- find out why we need this registry in the first place i.e. what could happen 
if we have multiple DataFormat instances with overlapping ids.
- if the registry is needed add a new factory method, something like 
`DataFormat::of(String ...)` which is properly synchronized. This method will 
be called by the constructor to retain the backward compatibility.
- deprecate (possibly for removal) `DataFormat::lookupMimeType(String)`, or 
keep it but have it properly synchronized

### Dangers

1. adding synchronization might lead to deadlocks if the application or library 
has existing code synchronized around some other object and not 
`DataFormat.class`.
2. removing the public constructor is a very visible, breaking change


### Alternatives

We could possibly prevent the application code creating `DataFormat`s with 
multiple ids by 
- creating a `public DataFormat(String)` constructor
- allowing multiple instances with the same id by removing the registry or 
using the registry only for `DataFormat`s with multiple ids

Or, deprecate (not for removal) the `public DataFormat(@NamedArg("ids") 
String... ids)` constructor for backward compatibility (also allowing the 
issues listed above), while adding `DataHandler.of(String)` factory for those 
applications that want to guarantee the absence of  these issues.

-------------

Commit messages:
 - Merge branch 'master' into 8373452.data.format
 - junit
 - whitespace
 - javadoc
 - data format

Changes: https://git.openjdk.org/jfx/pull/2006/files
  Webrev: https://webrevs.openjdk.org/?repo=jfx&pr=2006&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8373452
  Stats: 158 lines in 10 files changed: 82 ins; 35 del; 41 mod
  Patch: https://git.openjdk.org/jfx/pull/2006.diff
  Fetch: git fetch https://git.openjdk.org/jfx.git pull/2006/head:pull/2006

PR: https://git.openjdk.org/jfx/pull/2006

Reply via email to