<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>As there have not been any more comments so far in the past
      weeks, I assume there is common agreement with my current
      proposal.</p>
    <p>If not, please chime in ASAP. If there are no further comments, I
      will continue with the existing PR next.</p>
    <p>-Markus</p>
    <p><br>
    </p>
    <div class="moz-cite-prefix">Am 31.12.2024 um 14:43 schrieb Markus
      KARG:<br>
    </div>
    <blockquote type="cite"
      cite="mid:6ed5af05-6cc8-46ae-8ef4-4dbbc7f410e8@headcrashing.eu">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <p>Hi Chen,</p>
      <p>thank you for your ideas!</p>
      <p>Actually I cannot see what is "safer" in your proposal, but
        maybe I am missing to see a hidden risk in instanceof. Can you
        please outline the potential risk you actually see in "if
        (appendable implements Flushable f) f.flush();"?</p>
      <p>I mean, Flushable and Closable are simply *mix-ins* existing
        for exactly the purpose of "flushing-if-flusing-is-supported"
        and "closing-if-closing-is-supported", which is what we do need
        right here. Nobody wants to pass in a standalone "flusher" or
        standalone "closer" in addition to the actual object to flush
        and close, i. e., the Appendable. In particular, nobody actually
        reported the need to build a Writer from three distrinct
        implementation objects (or I missed this need). Explicitly
        passing "null" feels rather unintuitive and IMHO is doubtful.
        Why should someone want to do that? Again, apparently you see
        that use case, so if you really have strong feelings, then
        please make me understand who needs that and for what actual
        purpose.  :-)<br>
      </p>
      <p>To be all on the same side, again, please always share the core
        idea that this API more or less solely is the combination of
        "Writer.of(StringBuilder)" with "Writer.of(StringBuffer)" and
        "Writer.of(CharSet)".<br>
      </p>
      <p>Note that the sole target still is to pass in a StringBuilder,
        StringWriter, or CharBuffer, as wrapping *them* is *the driver*
        for the new API. While someone *can* do that, it is *not the
        target* of this API to pass in any Writer or any arbitrary
        Appendable. Therefore, we just need to be able *to deal with
        that case* once it happens -- which is why it is IMHO absolutely
        fine to directly return Writers *non-wrapped*. The API so far
        just says, "passing a Writer in turn returns a Writer", but it
        does *not* propose to enhance or limit that Writer in any way,
        and that is why it is (IMHO absolutely) safe to check all other
        Appendables for *their* actual ability to get flushed or to get
        closed. Remember, the target of *this* API proposal is *not* to
        be able to write into any Flushable-and-Closable-Appendable
        *without* flushing or closing it. Having that said, *I do not
        veto* adding an *additional* method like Writer.of(Appendable,
        boolean preventFlush, preventClose) *later* **if needed**, but
        IMHO that should rather be *separate* wrappers like
        Writer.withoutFlushing(Writer) and Writer.withoutClosing(Writer)
        (either you have the need to not-flush/not-close, or you don't
        have it, so it is not a special case of *this* API), or
        something like that, which both are, again, *non-targets* of my
        current proposal. In fact I still do not see *any* benefit of
        passing in a Writer into Writer.of(), neither as a single
        reference, nor split up into three interfaces (and BTW, I did
        *not* say a Writer is a combination of Appendable, Flushable and
        Closable). Neither do I see *any benefit* of being able to pass
        in in three different implementation objects. But what I do see
        in your proposal actually is:</p>
      <p>* It would make up a can of worms due to the possibility of
        providing three different implementation objects for that three
        parameters. Someone could do Writer.of(new StringBuilder(),
        Files.newBufferedWriter(), new CharBuffer()) and the outcome
        would be rather dubious (and mostly useless but confusing).<br>
      </p>
      <p>* As the sole target is to allow wrapping StringBuilder,
        StringWriter, and CharBuffer, and as we solely came to Flushable
        and Closable due to the question about "How to call flush and
        close ON THE PASSED REFERENCE, IFF the Appendable implements
        them?" it would be a real pain for alle users to be FORCED to
        repeat the same object three times.</p>
      <p>Having said that, my proposal is (as this is it what is IMHO
        mostly intuitive and most wanted):</p>
      <p>* Let's have solely Writer.of(Appendable) without any other
        parameters *in the first PR*; discuss the use case of more
        parameters *in subsequent PRs* IFF NEEDED as these should be
        *additional* method signatures to not torture the 90% standard
        case users with parameters they never need.<br>
      </p>
      <p>* Let's return Writer non-wrapped, and clearly document that in
        the JavaDocs. Have separate discussions about
        Writer.withoutFlushing(Writer) and Writer.withourClosing(Writer)
        *in subsequent threads* IFF NEEDED.<br>
      </p>
      <p>* Let's use "if (appendable instanceof Flushable f) f.flush()"
        and "if (appendable instanceof Closebale c) c.close()", and
        clearly document that in the JavaDocs. In case users do really
        want non-flushed, non-closed appendables wrapped as Writer, they
        do not lose something, but have to wait for the outcome of
        *subsequent* discussions about *additional* wrappers.</p>
      <p>I think that could be a clean, safe and straightforward way
        towards the replacement of StringWriter.<br>
      </p>
      <p>Regards and a happy new year! :-)</p>
      <p>-Markus</p>
      <p><br>
      </p>
      <div class="moz-cite-prefix">Am 31.12.2024 um 06:42 schrieb Chen
        Liang:<br>
      </div>
      <blockquote type="cite"
cite="mid:CABe8uE0Err3uUtkDqMxeVArp0LHKzeSr5k03zFG7VwswwdWZ3A@mail.gmail.com">
        <meta http-equiv="content-type"
          content="text/html; charset=UTF-8">
        <div dir="ltr">Hi Markus,
          <div>Thanks for your analysis that a Writer can be seen as a
            composition as an Appendable, a Flushable, and a Closeable.</div>
          <div>Given this view, I think we should add a
            Writer.of(Appenable, Flushable, Closeable) to specify the 3
            component behaviors of the returned writer.</div>
          <div>Each of the 3 arguments can be null, so that component
            will be no-op (Writer's Appendable methods only need to
            trivially return the Writer itself; all other methods return
            void).</div>
          <div>We will always require all 3 arguments to be passed; a
            null component means the caller knowingly demands no-op
            behavior for that component.</div>
          <div>I believe this approach would be safer, and avoids the
            accidental delegation of unwanted features from a given
            input Appendable when it happens to duck type.</div>
          <div><br>
          </div>
          <div>Regards,</div>
          <div>Chen Liang</div>
        </div>
        <br>
        <div class="gmail_quote gmail_quote_container">
          <div dir="ltr" class="gmail_attr">On Sat, Dec 28, 2024 at
            10:41 PM Markus KARG <<a
              href="mailto:markus@headcrashing.eu"
              moz-do-not-send="true" class="moz-txt-link-freetext">markus@headcrashing.eu</a>>
            wrote:<br>
          </div>
          <blockquote class="gmail_quote"
style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
            <div>
              <p>Chen,</p>
              <p>thank you for your comments! My ideas to address them
                are:</p>
              <p>* flush(): If the Appendable implements Flushable, then
                perform Flushable.flush() on it. Otherwise,
                Writer.flush() will be a no-op (besides checking if
                Writer is open).</p>
              <p>* close(): If the Appendable implements Closeable, then
                perform Closeable.close() on it. Otherwise,
                Writer.close() will be a no-op (besides calling
                this.flush() if open, and internally marking itself as
                closed).</p>
              <p>* Writer.of(Writer): The original sense of the new API
                is to create a Writer wrapping non-Writers like
                StringBuilder, CharBuffer etc., but not to reduce a
                Writer to an Appendable (that would rather be
                Appendable.narrow(Writer) or so). IMHO there is neither
                any need nor benefit to return a limited Writer instead
                of the actual writer. So actually I would plea for
                directly returning the given writer itself, so
                Writer.of(Writer) is a no-op. I do not see why someone
                would intentionally pass in a Writer in the hope to get
                back a more limited, non-flushing / non-closing variant
                of it, and I have a bad feeling about returning a Writer
                which is deliberately cutting away the ability to flush
                and close without any technical need. Maybe you could
                elaborate on your idea if you have strong feelings about
                that use case?</p>
              <p>* StringWriter: Writer.of() is -by intention- not a
                "fire and forget" drop-in replacement, but a "real"
                Writer. It comes with a price, but in do not see a big
                problem here. If one is such happy with StringWriter
                that dealing with IOException would be a no-go, then
                simply keep the app as-is. But if one really wants the
                benefits provided by Writer.of(), then dealing with
                IOExcpetion should be worth it. This is a (IMHO very)
                low price the programmer has to pay for the benefit of
                gaining non-sync, non-copy behavior. In most code using
                StringWriter I have seen so far, IOException was dealt
                with anyways, as the code was mostly IO-bound already
                (it expects "some" Writer, not a StringWriter, as it
                wants to perform I/O, but the target is "by incident" a
                String).</p>
              <p>To sum up: IMHO still it sounds feasible and the
                benefits outweigh the costs. :-)<br>
              </p>
              <p>-Markus</p>
              <p><br>
              </p>
              <div>Am 28.12.2024 um 01:51 schrieb Chen Liang:<br>
              </div>
              <blockquote type="cite">
                <div dir="ltr">
                  <p dir="ltr">Hi Markus,<br>
                    I think the idea makes sense, but it comes with more
                    difficulties than in the case of Reader.of. An
                    Appendable is a higher abstraction modeling only the
                    character writing aspects, without concerns with
                    resource control (such as flush or close).</p>
                  <p dir="ltr">One detail of note is that Writer itself
                    implements Appendable, but I don't think the new
                    method should return a Writer as-is; I think it
                    should return another writer whose close will not
                    close the underlying writer as we are only modelling
                    the appendable behavior without exporting the
                    resource control methods. Not sure about flush.</p>
                  <p>One use case you have mentioned is StringWriter.
                    StringWriter is distinct from StringReader: its
                    write and append methods do not throw IOE while the
                    base Writer does. So Writer.of cannot adequately
                    replace StringWriter without use-site ugliness,
                    until we have generic types that represent the
                    bottom type.</p>
                  <p>Regards,</p>
                  <p>Chen Liang</p>
                </div>
                <br>
                <div class="gmail_quote">
                  <div dir="ltr" class="gmail_attr">On Fri, Dec 20,
                    2024, 11:12 PM Markus KARG <<a
                      href="mailto:markus@headcrashing.eu"
                      target="_blank" moz-do-not-send="true"
                      class="moz-txt-link-freetext">markus@headcrashing.eu</a>>
                    wrote:<br>
                  </div>
                  <blockquote class="gmail_quote"
style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Dear
                    Sirs,<br>
                    <br>
                    JDK 24 comes with Reader.of(CharSequence), now let's
                    provide the <br>
                    symmetrical counterpart Writer.of(Appendable) in JDK
                    25! :-)<br>
                    <br>
                    For performance reasons, hereby I like to propose
                    the new public factory <br>
                    method Writer.of(Appendable). This will provide the
                    same benefits for <br>
                    writing, that Reader.of(CharSequence) provides for
                    reading since JDK 24 <br>
                    (see JDK-8341566). Before sharing a pull request,
                    I'd kindly like to <br>
                    request for comments.<br>
                    <br>
                    Since Java 1.1 we have the StringWriter class. Since
                    Java 1.5 we have <br>
                    the Appendable interface. StringBuilder,
                    StringBuffer and CharBuffer are <br>
                    first-class implementations of it in the JDK, and
                    there might exist <br>
                    third-party implementations of non-String text
                    sinks. Until today, <br>
                    however, we do not have a Writer for Appendables,
                    but need to go costly <br>
                    detours.<br>
                    <br>
                    Text sinks in Java are expected to implement the
                    Writer interface. <br>
                    Libraries and frameworks expect application code to
                    provide Writers to <br>
                    consume text produced by the library or framework,
                    for example. <br>
                    Application code often wants to modify the received
                    text, e. g. embed <br>
                    received SVG text into in a larger HTML text
                    document, or simply forward <br>
                    the text as-is to I/O, so StringBuilder or
                    CharBuffer is what the <br>
                    application code actually uses, but not Strings! In
                    such cases, taking <br>
                    the StringWriter.toString() detour is common but
                    inefficient: It implies <br>
                    duplicating the COMPLETE text for the sole sake of
                    creating a temporary <br>
                    String, while the subsequent processing will copy
                    the data anyways or <br>
                    just uses a small piece of it. This eats up time and
                    memory uselessly, <br>
                    and increases GC pressure. Also, StringWriter is
                    synchronized (not <br>
                    explicitly, but de-facto, as it uses StringBuffer),
                    which implies <br>
                    another needless slowdown. In many cases, the
                    synchronization has no use <br>
                    at all, as in real-world applications least Writers
                    are actually <br>
                    accessed concurrently. As a result, today the major
                    benefit of <br>
                    StringBuilder over StringBuffer (being
                    non-synchronized) vanishes as <br>
                    soon as a StringWriter is used to provide its
                    content. This means, <br>
                    "stringBuilder.append(stringWriter.toString())"
                    imposes slower <br>
                    performance than essentially needed, in two ways:
                    toString(), synchronized.<br>
                    <br>
                    In an attempt to improve performance of this rather
                    typical use case, I <br>
                    like to contribute a pull request providing the new
                    public factory <br>
                    method java.io.Writer.of(Appendable). This is
                    symmetrical to the <br>
                    solution we implemented in JDK-8341566 for the
                    reversed case: <br>
                    java.io.Reader.of(CharSequence).<br>
                    <br>
                    My idea is to mostly copy the existing code of
                    StringWriter, but wrap a <br>
                    caller-provided Appendable instead of an internally
                    created <br>
                    StringBuilder; this strips synchronization; then add
                    optimized use for <br>
                    the StringBuffer, StringBuilder and CharBuffer
                    implementations (in the <br>
                    sense of write(char[],start,end) to prevent a
                    char-by-char loop in these <br>
                    cases).<br>
                    <br>
                    Alternatives:<br>
                    <br>
                    - Applications could use Apache Commons IO's
                    StringBuilderWriter, which <br>
                    is limited to StringBuilder, so is not usable for
                    the CharBuffer or <br>
                    custom Appendable case. As it is an open-source
                    third-party dependency, <br>
                    some authors might not be allowed to use it, or may
                    not want to carry <br>
                    this additional burden just for the sake of this
                    single performance <br>
                    improvement. In addition, this library is not
                    actively modernized; its <br>
                    Java baseline still is Java 8. There is no
                    commercial support.<br>
                    <br>
                    - Applications could write their own Writer
                    implementation. Given the <br>
                    assumption that this is a rather common use case,
                    this imposes <br>
                    unjustified additional work for the authors of
                    thousands of <br>
                    applications. It is hard to justify why there is a
                    StringWriter but not <br>
                    a Writer for other Appendables.<br>
                    <br>
                    - Instead of writing a new Writer factory method, we
                    could slightly <br>
                    modify StringWriter, so it uses StringBuilder
                    (instead of StringBuffer). <br>
                    This (still) results in unnecessary duplication of
                    the full text at <br>
                    toString() and (now also) at getBuffer(), and it
                    will break existing <br>
                    applications due the missing synchronization.<br>
                    <br>
                    - Instead of writing a new Writer factory method, we
                    could write a new <br>
                    AppendableWriter class. This piles up the amount of
                    public classes, <br>
                    which was the main reason in JDK-8341566 to go with
                    the <br>
                    "Reader.of(CharSequence)" factory method instead of
                    the <br>
                    "CharSequenceReader" class. Also it would be
                    confusing to have <br>
                    Reader.of(...) but not Writer.of(...) in the API.<br>
                    <br>
                    - We could go with a specific Appendable class (like
                    StringBuilder) <br>
                    instead of supporting all Appendable
                    implementations. This would reduce <br>
                    the number of applicable use cases daramatically (in
                    particular as <br>
                    CharBuffer is not supported any more) without
                    providing any considerable <br>
                    benefit (other than making the OpenJDK-internal
                    source code a bit <br>
                    shorter). In particular it makes it impossible to
                    opt-in for the below <br>
                    option:<br>
                    <br>
                    Option:<br>
                    <br>
                    - Once we have Writer.of(Appendable), we could
                    replace the full <br>
                    implementation of StringWriter by synchronized calls
                    to the new Writer. <br>
                    This would reduce duplicate code.<br>
                    <br>
                    Kindly requesting comments.<br>
                    <br>
                    -Markus Karg<br>
                    <br>
                  </blockquote>
                </div>
              </blockquote>
            </div>
          </blockquote>
        </div>
      </blockquote>
    </blockquote>
  </body>
</html>