On Sat, 19 Sep 2026 10:05:35 GMT, Tagir F. Valeev <[email protected]> wrote:

>> Three options were suggested about the new API:
>> - ListFormat.formatting()
>> - ListFormat.toCollector()
>> - Collectors.listFormatting(ListFormat) static method
>> I feel it should belong to ListFormat, but I'm open to discussions.
>> 
>> For testing, I tested a couple of scenarios and a few corner cases (empty 
>> stream, null element, parallel stream) in a simple non-parameterized JUnit 
>> test. I believe that it's enough, given that the implementation is trivial, 
>> and ListFormat itself, as well as Stream API is already thoroughly tested by 
>> other tests. Tell me if you feel that some testing scenarios are missing, 
>> I'll happily add them.
>> 
>> ---------
>> - [x] I confirm that I make this contribution in accordance with the 
>> [OpenJDK Interim AI Policy](https://openjdk.org/legal/ai).
>
> Tagir F. Valeev has updated the pull request incrementally with one 
> additional commit since the last revision:
> 
>   8391789: Dumb error fix

A quick note on use cases. I can provide use cases from our codebase (IntelliJ 
IDEA) where the collector was involved. We used `ListFormat` from icu4j to join 
lists and streams for years, and recently switched to JDK implementation. In 
our case, we still need separate methods because a globally known current 
`Locale` should be substituted automatically (it differs from 
`Locale.getDefault()` and depends on which language pack is activated by the 
user). As I said before, we limit ourselves to three formats: wide-and, wide-or 
and narrow-and.

1. Wide-and -- formatting of an error message in Javadoc snippets:

JavaBundle.message(
          "javadoc.snippet.error.unknown.enum.value",
          tagName, attribute.key(), attribute.value(),
          Stream.of(enumClass.getEnumConstants()).map(val -> "'" + 
val.name().toLowerCase(Locale.ROOT) + "'")
            .collect(NlsMessages.joiningAnd()))))
// in resources:
javadoc.snippet.error.unknown.enum.value=@{0}: unknown {1} ''{2}''; only {3} 
are supported

Possible result (from test-data):

@link: unknown type 'none'; only 'link' and 'linkplain' are supported
@highlight: unknown type 'underlined'; only 'bold', 'italic', and 'highlighted' 
are supported

The code is somewhat overly generic; it is parameterized by enumClass, which is 
either `public enum HighlightType {BOLD, ITALIC, HIGHLIGHTED}` or `public enum 
LinkType {LINK, LINKPLAIN}`, no other options right now.

2. Wide-and -- formatting inspection message that suggests adding more steps to 
the existing Stream API call:

String fusedSteps = 
newTerminal.fusedElements().collect(NlsMessages.joiningAnd());
JavaBundle.message("inspection.fuse.stream.operations.fix.name", fusedSteps);
// in resources:
inspection.fuse.stream.operations.fix.name=Fuse {0} into the Stream API chain

Possible result:

Fuse ArrayList, 'sort', and 'toArray' into the Stream API chain
Fuse 'sort' into the Stream API chain
Fuse HashSet and ArrayList into the Stream API chain


3. Wide-and -- formatting inspection message reporting unreachable catch 
messages:

String types = 
caughtCopy.stream().map(JavaHighlightUtil::formatType).collect(NlsMessages.joiningAnd());
String message = 
JavaAnalysisBundle.message("inspection.unreachable.catch.message", types, 
caughtCopy.size());
// in resources:
inspection.unreachable.catch.message=Unreachable section: {1, choice, 
0#exception|2#exceptions} ''{0}'' {1, choice, 0#has|2#have} already been caught

Possible result:

Unreachable section: exception 'java.io.FileNotFoundException' has already been 
caught
Unreachable section: exceptions 'C.E1' and 'C.E2' have already been caught


4. Wide-or -- intention action description that suggests converting a number to 
various other formats:

String options = 
chooser.actions().stream().map(ModCommandAction::getFamilyName).collect(NlsMessages.joiningOr());
LangBundle.message("intention.name.convert.number.to.with.text.preview", 
options)
// in resources:
intention.name.convert.number.to.with.text.preview=Convert number to {0}

Possible result:

Convert number to hex, binary, or octal

List of choices may differ based on the input number, its type and Java version 
used (e.g., no 'binary' option on Java 6).

5. Wide-or -- data-flow analysis information which shows statically known 
possible values of a variable:

values.stream().map(DfaPsiUtil::renderValue).sorted().collect(NlsMessages.joiningOr())

For example, if you have the code like this:

  void test(String str) {
    if (str.equals("hello") || str.equals("world")) {
      System.out.println(str);
    }
  }

And you press Ctrl+Shift+P twice on 'str' inside 'if', you'll see `Value: 
"hello" or "world"`

6. Narrow-and -- 'Build modules' menu item in the popup menu:

String moduleNames = Stream.of(modules).map(m -> 
"'"+m.getName()+"'").collect(NlsMessages.joiningNarrowAnd());
presentationText = moduleNames.length() > 20 ?
                       
JavaCompilerBundle.message("action.make.selected.modules.text") :
                       
JavaCompilerBundle.message("action.make.few.modules.text", moduleNames);
// in resources:
action.make.selected.modules.text=Build Selected _Modules
action.make.few.modules.text=Build _Modules {0}

A single module case is handled separately. Here, we prefer to keep the list 
narrow because the horizontal space in the popup menu item is kinda limited. 
Still, it's better to use localized joining over simply `joining(", ")`, 
because we want it to look nicely, e.g., in Japanese locale (using "、" instead 
of ", ").

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

PR Comment: https://git.openjdk.org/jdk/pull/32716#issuecomment-5777500479

Reply via email to