001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.io;
018
019
020import java.io.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022
023/**
024 * A wrapper around a {@link Writer} that prevents the underlying writer from being closed.
025 *
026 * <p>
027 * This class wraps an existing {@link Writer} and intercepts the {@link #close()} method,
028 * making it a no-op (except for flushing). All other operations are delegated to the underlying
029 * writer. This is useful when you need to pass a writer to code that will close it, but you
030 * want to keep the underlying writer open for further use.
031 *
032 * <h5 class='section'>Features:</h5>
033 * <ul class='spaced-list'>
034 *    <li>Prevents closing - {@link #close()} flushes but doesn't close the underlying writer
035 *    <li>Transparent delegation - all other operations pass through to the wrapped writer
036 *    <li>Useful for resource management - allows multiple consumers without premature closing
037 * </ul>
038 *
039 * <h5 class='section'>Use Cases:</h5>
040 * <ul class='spaced-list'>
041 *    <li>Passing writers to APIs that close them, but you need to keep the writer open
042 *    <li>Multiple operations on the same writer where intermediate operations might close it
043 *    <li>Resource management scenarios where you control the writer lifecycle
044 *    <li>Wrapping system writers that should not be closed
045 * </ul>
046 *
047 * <h5 class='section'>Usage:</h5>
048 * <p class='bjava'>
049 *    <jc>// Wrap a writer that should not be closed</jc>
050 *    FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"output.txt"</js>);
051 *    NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
052 *
053 *    <jc>// Pass to code that might close it</jc>
054 *    <jv>someMethod</jv>(<jv>wrapper</jv>);  <jc>// May call close(), but fw remains open</jc>
055 *
056 *    <jc>// Continue using the original writer</jc>
057 *    <jv>fw</jv>.write(<js>"more data"</js>);
058 *    <jv>fw</jv>.close();  <jc>// Close when actually done</jc>
059 * </p>
060 *
061 * <h5 class='section'>Important Notes:</h5>
062 * <ul class='spaced-list'>
063 *    <li>The {@link #close()} method flushes the writer but does not close the underlying writer
064 *    <li>You are responsible for closing the underlying writer when you're done with it
065 *    <li>This wrapper does not prevent resource leaks - ensure the underlying writer is eventually closed
066 * </ul>
067 *
068 * <h5 class='section'>See Also:</h5><ul>
069 *    <li class='jc'>{@link NoCloseOutputStream} - OutputStream counterpart
070 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsIO">I/O Package</a>
071 * </ul>
072 */
073@SuppressWarnings({
074   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
075   "resource" // Intentionally not owned; this wrapper deliberately does not close the underlying writer
076})
077public class NoCloseWriter extends Writer {
078
079   private final Writer w;
080
081   /**
082    * Constructor.
083    *
084    * <p>
085    * Creates a new NoCloseWriter that wraps the specified Writer. The wrapper will prevent
086    * the underlying writer from being closed via the {@link #close()} method.
087    *
088    * <h5 class='section'>Example:</h5>
089    * <p class='bjava'>
090    *    FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>);
091    *    NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
092    * </p>
093    *
094    * @param w The Writer to wrap. Must not be <jk>null</jk>.
095    */
096   public NoCloseWriter(Writer w) {
097      this.w = reqnn("w", w);
098   }
099
100   @Override /* Overridden from Writer */
101   public Writer append(char c) throws IOException {
102      return w.append(c);
103   }
104
105   @Override /* Overridden from Writer */
106   public Writer append(CharSequence csq) throws IOException {
107      return w.append(csq);
108   }
109
110   @Override /* Overridden from Writer */
111   public Writer append(CharSequence csq, int start, int end) throws IOException {
112      return w.append(csq, start, end);
113   }
114
115   /**
116    * Flushes the writer but does not close the underlying Writer.
117    *
118    * <p>
119    * This method flushes any buffered data to the underlying writer but does not close it.
120    * The underlying writer remains open and can continue to be used after this method is called.
121    *
122    * <h5 class='section'>Example:</h5>
123    * <p class='bjava'>
124    *    FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>);
125    *    NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
126    *    <jv>wrapper</jv>.close();  <jc>// Flushes but doesn't close fw</jc>
127    *    <jv>fw</jv>.write(<js>"still works"</js>);  <jc>// fw is still open</jc>
128    * </p>
129    *
130    * @throws IOException If an I/O error occurs while flushing.
131    */
132   @Override /* Overridden from Writer */
133   public void close() throws IOException {
134      w.flush();
135   }
136
137   @Override /* Overridden from Writer */
138   public void flush() throws IOException {
139      w.flush();
140   }
141
142   @Override /* Overridden from Object */
143   public String toString() {
144      return w.toString();
145   }
146
147   @Override /* Overridden from Writer */
148   public void write(char[] cbuf) throws IOException {
149      reqnn("cbuf", cbuf);
150      w.write(cbuf);
151   }
152
153   @Override /* Overridden from Writer */
154   public void write(char[] cbuf, int off, int len) throws IOException {
155      reqnn("cbuf", cbuf);
156      w.write(cbuf, off, len);
157   }
158
159   @Override /* Overridden from Writer */
160   public void write(int c) throws IOException {
161      w.write(c);
162   }
163
164   @Override /* Overridden from Writer */
165   public void write(String str) throws IOException {
166      reqnn("str", str);
167      w.write(str);
168   }
169
170   @Override /* Overridden from Writer */
171   public void write(String str, int off, int len) throws IOException {
172      reqnn("str", str);
173      w.write(str, off, len);
174   }
175}