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}