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
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020
021import java.io.*;
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 */
073public class NoCloseWriter extends Writer {
074
075   private final Writer w;
076
077   /**
078    * Constructor.
079    *
080    * <p>
081    * Creates a new NoCloseWriter that wraps the specified Writer. The wrapper will prevent
082    * the underlying writer from being closed via the {@link #close()} method.
083    *
084    * <h5 class='section'>Example:</h5>
085    * <p class='bjava'>
086    *    FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>);
087    *    NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
088    * </p>
089    *
090    * @param w The Writer to wrap. Must not be <jk>null</jk>.
091    */
092   public NoCloseWriter(Writer w) {
093      this.w = assertArgNotNull("w", w);
094   }
095
096   @Override /* Overridden from Writer */
097   public Writer append(char c) throws IOException {
098      return w.append(c);
099   }
100
101   @Override /* Overridden from Writer */
102   public Writer append(CharSequence csq) throws IOException {
103      return w.append(csq);
104   }
105
106   @Override /* Overridden from Writer */
107   public Writer append(CharSequence csq, int start, int end) throws IOException {
108      return w.append(csq, start, end);
109   }
110
111   /**
112    * Flushes the writer but does not close the underlying Writer.
113    *
114    * <p>
115    * This method flushes any buffered data to the underlying writer but does not close it.
116    * The underlying writer remains open and can continue to be used after this method is called.
117    *
118    * <h5 class='section'>Example:</h5>
119    * <p class='bjava'>
120    *    FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>);
121    *    NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
122    *    <jv>wrapper</jv>.close();  <jc>// Flushes but doesn't close fw</jc>
123    *    <jv>fw</jv>.write(<js>"still works"</js>);  <jc>// fw is still open</jc>
124    * </p>
125    *
126    * @throws IOException If an I/O error occurs while flushing.
127    */
128   @Override /* Overridden from Writer */
129   public void close() throws IOException {
130      w.flush();
131   }
132
133   @Override /* Overridden from Writer */
134   public void flush() throws IOException {
135      w.flush();
136   }
137
138   @Override /* Overridden from Object */
139   public String toString() {
140      return w.toString();
141   }
142
143   @Override /* Overridden from Writer */
144   public void write(char[] cbuf) throws IOException {
145      assertArgNotNull("cbuf", cbuf);
146      w.write(cbuf);
147   }
148
149   @Override /* Overridden from Writer */
150   public void write(char[] cbuf, int off, int len) throws IOException {
151      assertArgNotNull("cbuf", cbuf);
152      w.write(cbuf, off, len);
153   }
154
155   @Override /* Overridden from Writer */
156   public void write(int c) throws IOException {
157      w.write(c);
158   }
159
160   @Override /* Overridden from Writer */
161   public void write(String str) throws IOException {
162      assertArgNotNull("str", str);
163      w.write(str);
164   }
165
166   @Override /* Overridden from Writer */
167   public void write(String str, int off, int len) throws IOException {
168      assertArgNotNull("str", str);
169      w.write(str, off, len);
170   }
171}