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 * https://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.commons.io.output; 018 019import java.io.FilterWriter; 020import java.io.IOException; 021import java.io.Writer; 022 023import org.apache.commons.io.IOUtils; 024 025/** 026 * A writer proxy which delegates to the wrapped writer. 027 * <p> 028 * It is an alternative base class to FilterWriter to increase reusability, because FilterWriter changes the methods being called, such as 029 * {@code write(char[]) to write(char[], int, int)} and {@code write(String) to write(String, int, int)}. 030 * </p> 031 */ 032public class ProxyWriter extends FilterWriter { 033 034 /** 035 * Constructs a new ProxyWriter. 036 * 037 * @param delegate the Writer to delegate to. 038 */ 039 public ProxyWriter(final Writer delegate) { 040 // the delegate is stored in a protected superclass variable named 'out' 041 super(delegate); 042 } 043 044 /** 045 * Invoked by the write methods after the proxied call has returned 046 * successfully. The number of chars written (1 for the 047 * {@link #write(int)} method, buffer length for {@link #write(char[])}, 048 * etc.) is given as an argument. 049 * <p> 050 * Subclasses can override this method to add common post-processing 051 * functionality without having to override all the write methods. 052 * The default implementation does nothing. 053 * </p> 054 * 055 * @param n number of chars written. 056 * @throws IOException if the post-processing fails. 057 * @since 2.0 058 */ 059 @SuppressWarnings("unused") // Possibly thrown from subclasses. 060 protected void afterWrite(final int n) throws IOException { 061 // noop 062 } 063 064 /** 065 * Invokes the delegate's {@code append(char)} method. 066 * 067 * @param c The character to write. 068 * @return this writer. 069 * @throws IOException if an I/O error occurs. 070 * @since 2.0 071 */ 072 @Override 073 public Writer append(final char c) throws IOException { 074 try { 075 beforeWrite(1); 076 out.append(c); 077 afterWrite(1); 078 } catch (final IOException e) { 079 handleIOException(e); 080 } 081 return this; 082 } 083 084 /** 085 * Invokes the delegate's {@code append(CharSequence)} method. 086 * 087 * @param csq The character sequence to write. 088 * @return this writer. 089 * @throws IOException if an I/O error occurs. 090 * @since 2.0 091 */ 092 @Override 093 public Writer append(final CharSequence csq) throws IOException { 094 try { 095 final int len = IOUtils.length(csq); 096 beforeWrite(len); 097 out.append(csq); 098 afterWrite(len); 099 } catch (final IOException e) { 100 handleIOException(e); 101 } 102 return this; 103 } 104 105 /** 106 * Invokes the delegate's {@code append(CharSequence, int, int)} method. 107 * 108 * @param csq The character sequence to write. 109 * @param start The index of the first character to write. 110 * @param end The index of the first character to write (exclusive). 111 * @return this writer. 112 * @throws IOException if an I/O error occurs. 113 * @since 2.0 114 */ 115 @Override 116 public Writer append(final CharSequence csq, final int start, final int end) throws IOException { 117 try { 118 beforeWrite(end - start); 119 out.append(csq, start, end); 120 afterWrite(end - start); 121 } catch (final IOException e) { 122 handleIOException(e); 123 } 124 return this; 125 } 126 127 /** 128 * Invoked by the write methods before the call is proxied. The number 129 * of chars to be written (1 for the {@link #write(int)} method, buffer 130 * length for {@link #write(char[])}, etc.) is given as an argument. 131 * <p> 132 * Subclasses can override this method to add common pre-processing 133 * functionality without having to override all the write methods. 134 * The default implementation does nothing. 135 * </p> 136 * 137 * @param n number of chars to be written. 138 * @throws IOException if the pre-processing fails. 139 * @since 2.0 140 */ 141 @SuppressWarnings("unused") // Possibly thrown from subclasses. 142 protected void beforeWrite(final int n) throws IOException { 143 // noop 144 } 145 146 /** 147 * Invokes the delegate's {@code close()} method. 148 * 149 * @throws IOException if an I/O error occurs. 150 */ 151 @Override 152 public void close() throws IOException { 153 IOUtils.close(out, this::handleIOException); 154 } 155 156 /** 157 * Invokes the delegate's {@code flush()} method. 158 * 159 * @throws IOException if an I/O error occurs. 160 */ 161 @Override 162 public void flush() throws IOException { 163 try { 164 out.flush(); 165 } catch (final IOException e) { 166 handleIOException(e); 167 } 168 } 169 170 /** 171 * Handles any IOExceptions thrown. 172 * <p> 173 * This method provides a point to implement custom exception 174 * handling. The default behavior is to re-throw the exception. 175 * </p> 176 * 177 * @param e The IOException thrown. 178 * @throws IOException if an I/O error occurs. 179 * @since 2.0 180 */ 181 protected void handleIOException(final IOException e) throws IOException { 182 throw e; 183 } 184 185 /** 186 * Sets the underlying writer. 187 * <p> 188 * Use with caution. 189 * </p> 190 * 191 * @param out the underlying output writer. 192 * @return {@code this} instance. 193 * @since 2.22.0 194 */ 195 public ProxyWriter setReference(final Writer out) { 196 this.out = out; 197 return this; 198 } 199 200 /** 201 * Unwraps this instance by returning the underlying {@link Writer}. 202 * <p> 203 * Use with caution. 204 * </p> 205 * 206 * @return the underlying {@link Writer}. 207 * @since 2.22.0 208 */ 209 public Writer unwrap() { 210 return out; 211 } 212 213 /** 214 * Invokes the delegate's {@code write(char[])} method. 215 * 216 * @param cbuf the characters to write. 217 * @throws IOException if an I/O error occurs. 218 */ 219 @Override 220 public void write(final char[] cbuf) throws IOException { 221 try { 222 final int len = IOUtils.length(cbuf); 223 beforeWrite(len); 224 out.write(cbuf); 225 afterWrite(len); 226 } catch (final IOException e) { 227 handleIOException(e); 228 } 229 } 230 231 /** 232 * Invokes the delegate's {@code write(char[], int, int)} method. 233 * 234 * @param cbuf the characters to write. 235 * @param off The start offset. 236 * @param len The number of characters to write. 237 * @throws IOException if an I/O error occurs. 238 */ 239 @Override 240 public void write(final char[] cbuf, final int off, final int len) throws IOException { 241 try { 242 beforeWrite(len); 243 out.write(cbuf, off, len); 244 afterWrite(len); 245 } catch (final IOException e) { 246 handleIOException(e); 247 } 248 } 249 250 /** 251 * Invokes the delegate's {@code write(int)} method. 252 * 253 * @param c the character to write. 254 * @throws IOException if an I/O error occurs. 255 */ 256 @Override 257 public void write(final int c) throws IOException { 258 try { 259 beforeWrite(1); 260 out.write(c); 261 afterWrite(1); 262 } catch (final IOException e) { 263 handleIOException(e); 264 } 265 } 266 267 /** 268 * Invokes the delegate's {@code write(String)} method. 269 * 270 * @param str the string to write. 271 * @throws IOException if an I/O error occurs. 272 */ 273 @Override 274 public void write(final String str) throws IOException { 275 try { 276 final int len = IOUtils.length(str); 277 beforeWrite(len); 278 out.write(str); 279 afterWrite(len); 280 } catch (final IOException e) { 281 handleIOException(e); 282 } 283 } 284 285 /** 286 * Invokes the delegate's {@code write(String)} method. 287 * 288 * @param str the string to write. 289 * @param off The start offset. 290 * @param len The number of characters to write. 291 * @throws IOException if an I/O error occurs. 292 */ 293 @Override 294 public void write(final String str, final int off, final int len) throws IOException { 295 try { 296 beforeWrite(len); 297 out.write(str, off, len); 298 afterWrite(len); 299 } catch (final IOException e) { 300 handleIOException(e); 301 } 302 } 303 304}