View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.commons.io;
18  
19  import static org.apache.juneau.commons.utils.AssertionUtils.*;
20  
21  import java.io.*;
22  
23  /**
24   * A wrapper around a {@link Writer} that prevents the underlying writer from being closed.
25   *
26   * <p>
27   * This class wraps an existing {@link Writer} and intercepts the {@link #close()} method,
28   * making it a no-op (except for flushing). All other operations are delegated to the underlying
29   * writer. This is useful when you need to pass a writer to code that will close it, but you
30   * want to keep the underlying writer open for further use.
31   *
32   * <h5 class='section'>Features:</h5>
33   * <ul class='spaced-list'>
34   * 	<li>Prevents closing - {@link #close()} flushes but doesn't close the underlying writer
35   * 	<li>Transparent delegation - all other operations pass through to the wrapped writer
36   * 	<li>Useful for resource management - allows multiple consumers without premature closing
37   * </ul>
38   *
39   * <h5 class='section'>Use Cases:</h5>
40   * <ul class='spaced-list'>
41   * 	<li>Passing writers to APIs that close them, but you need to keep the writer open
42   * 	<li>Multiple operations on the same writer where intermediate operations might close it
43   * 	<li>Resource management scenarios where you control the writer lifecycle
44   * 	<li>Wrapping system writers that should not be closed
45   * </ul>
46   *
47   * <h5 class='section'>Usage:</h5>
48   * <p class='bjava'>
49   * 	<jc>// Wrap a writer that should not be closed</jc>
50   * 	FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"output.txt"</js>);
51   * 	NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
52   *
53   * 	<jc>// Pass to code that might close it</jc>
54   * 	<jv>someMethod</jv>(<jv>wrapper</jv>);  <jc>// May call close(), but fw remains open</jc>
55   *
56   * 	<jc>// Continue using the original writer</jc>
57   * 	<jv>fw</jv>.write(<js>"more data"</js>);
58   * 	<jv>fw</jv>.close();  <jc>// Close when actually done</jc>
59   * </p>
60   *
61   * <h5 class='section'>Important Notes:</h5>
62   * <ul class='spaced-list'>
63   * 	<li>The {@link #close()} method flushes the writer but does not close the underlying writer
64   * 	<li>You are responsible for closing the underlying writer when you're done with it
65   * 	<li>This wrapper does not prevent resource leaks - ensure the underlying writer is eventually closed
66   * </ul>
67   *
68   * <h5 class='section'>See Also:</h5><ul>
69   * 	<li class='jc'>{@link NoCloseOutputStream} - OutputStream counterpart
70   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsIO">I/O Package</a>
71   * </ul>
72   */
73  public class NoCloseWriter extends Writer {
74  
75  	private final Writer w;
76  
77  	/**
78  	 * Constructor.
79  	 *
80  	 * <p>
81  	 * Creates a new NoCloseWriter that wraps the specified Writer. The wrapper will prevent
82  	 * the underlying writer from being closed via the {@link #close()} method.
83  	 *
84  	 * <h5 class='section'>Example:</h5>
85  	 * <p class='bjava'>
86  	 * 	FileWriter <jv>fw</jv> = <jk>new</jk> FileWriter(<js>"file.txt"</js>);
87  	 * 	NoCloseWriter <jv>wrapper</jv> = <jk>new</jk> NoCloseWriter(<jv>fw</jv>);
88  	 * </p>
89  	 *
90  	 * @param w The Writer to wrap. Must not be <jk>null</jk>.
91  	 */
92  	public NoCloseWriter(Writer w) {
93  		this.w = assertArgNotNull("w", w);
94  	}
95  
96  	@Override /* Overridden from Writer */
97  	public Writer append(char c) throws IOException {
98  		return w.append(c);
99  	}
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 }