ProxyWriter.java

  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.commons.io.output;

  18. import java.io.FilterWriter;
  19. import java.io.IOException;
  20. import java.io.Writer;

  21. import org.apache.commons.io.IOUtils;

  22. /**
  23.  * A Proxy stream which acts as expected, that is it passes the method calls on to the proxied stream and doesn't
  24.  * change which methods are being called. It is an alternative base class to FilterWriter to increase reusability,
  25.  * because FilterWriter changes the methods being called, such as {@code write(char[]) to write(char[], int, int)}
  26.  * and {@code write(String) to write(String, int, int)}.
  27.  */
  28. public class ProxyWriter extends FilterWriter {

  29.     /**
  30.      * Constructs a new ProxyWriter.
  31.      *
  32.      * @param delegate  the Writer to delegate to
  33.      */
  34.     public ProxyWriter(final Writer delegate) {
  35.         // the delegate is stored in a protected superclass variable named 'out'
  36.         super(delegate);
  37.     }

  38.     /**
  39.      * Invoked by the write methods after the proxied call has returned
  40.      * successfully. The number of chars written (1 for the
  41.      * {@link #write(int)} method, buffer length for {@link #write(char[])},
  42.      * etc.) is given as an argument.
  43.      * <p>
  44.      * Subclasses can override this method to add common post-processing
  45.      * functionality without having to override all the write methods.
  46.      * The default implementation does nothing.
  47.      * </p>
  48.      *
  49.      * @param n number of chars written
  50.      * @throws IOException if the post-processing fails
  51.      * @since 2.0
  52.      */
  53.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  54.     protected void afterWrite(final int n) throws IOException {
  55.         // noop
  56.     }

  57.     /**
  58.      * Invokes the delegate's {@code append(char)} method.
  59.      * @param c The character to write
  60.      * @return this writer
  61.      * @throws IOException if an I/O error occurs.
  62.      * @since 2.0
  63.      */
  64.     @Override
  65.     public Writer append(final char c) throws IOException {
  66.         try {
  67.             beforeWrite(1);
  68.             out.append(c);
  69.             afterWrite(1);
  70.         } catch (final IOException e) {
  71.             handleIOException(e);
  72.         }
  73.         return this;
  74.     }

  75.     /**
  76.      * Invokes the delegate's {@code append(CharSequence)} method.
  77.      * @param csq The character sequence to write
  78.      * @return this writer
  79.      * @throws IOException if an I/O error occurs.
  80.      * @since 2.0
  81.      */
  82.     @Override
  83.     public Writer append(final CharSequence csq) throws IOException {
  84.         try {
  85.             final int len = IOUtils.length(csq);
  86.             beforeWrite(len);
  87.             out.append(csq);
  88.             afterWrite(len);
  89.         } catch (final IOException e) {
  90.             handleIOException(e);
  91.         }
  92.         return this;
  93.     }

  94.     /**
  95.      * Invokes the delegate's {@code append(CharSequence, int, int)} method.
  96.      * @param csq The character sequence to write
  97.      * @param start The index of the first character to write
  98.      * @param end  The index of the first character to write (exclusive)
  99.      * @return this writer
  100.      * @throws IOException if an I/O error occurs.
  101.      * @since 2.0
  102.      */
  103.     @Override
  104.     public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
  105.         try {
  106.             beforeWrite(end - start);
  107.             out.append(csq, start, end);
  108.             afterWrite(end - start);
  109.         } catch (final IOException e) {
  110.             handleIOException(e);
  111.         }
  112.         return this;
  113.     }

  114.     /**
  115.      * Invoked by the write methods before the call is proxied. The number
  116.      * of chars to be written (1 for the {@link #write(int)} method, buffer
  117.      * length for {@link #write(char[])}, etc.) is given as an argument.
  118.      * <p>
  119.      * Subclasses can override this method to add common pre-processing
  120.      * functionality without having to override all the write methods.
  121.      * The default implementation does nothing.
  122.      * </p>
  123.      *
  124.      * @param n number of chars to be written
  125.      * @throws IOException if the pre-processing fails
  126.      * @since 2.0
  127.      */
  128.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  129.     protected void beforeWrite(final int n) throws IOException {
  130.         // noop
  131.     }

  132.     /**
  133.      * Invokes the delegate's {@code close()} method.
  134.      * @throws IOException if an I/O error occurs.
  135.      */
  136.     @Override
  137.     public void close() throws IOException {
  138.         IOUtils.close(out, this::handleIOException);
  139.     }

  140.     /**
  141.      * Invokes the delegate's {@code flush()} method.
  142.      * @throws IOException if an I/O error occurs.
  143.      */
  144.     @Override
  145.     public void flush() throws IOException {
  146.         try {
  147.             out.flush();
  148.         } catch (final IOException e) {
  149.             handleIOException(e);
  150.         }
  151.     }

  152.     /**
  153.      * Handles any IOExceptions thrown.
  154.      * <p>
  155.      * This method provides a point to implement custom exception
  156.      * handling. The default behavior is to re-throw the exception.
  157.      * </p>
  158.      *
  159.      * @param e The IOException thrown
  160.      * @throws IOException if an I/O error occurs.
  161.      * @since 2.0
  162.      */
  163.     protected void handleIOException(final IOException e) throws IOException {
  164.         throw e;
  165.     }

  166.     /**
  167.      * Invokes the delegate's {@code write(char[])} method.
  168.      * @param cbuf the characters to write
  169.      * @throws IOException if an I/O error occurs.
  170.      */
  171.     @Override
  172.     public void write(final char[] cbuf) throws IOException {
  173.         try {
  174.             final int len = IOUtils.length(cbuf);
  175.             beforeWrite(len);
  176.             out.write(cbuf);
  177.             afterWrite(len);
  178.         } catch (final IOException e) {
  179.             handleIOException(e);
  180.         }
  181.     }

  182.     /**
  183.      * Invokes the delegate's {@code write(char[], int, int)} method.
  184.      * @param cbuf the characters to write
  185.      * @param off The start offset
  186.      * @param len The number of characters to write
  187.      * @throws IOException if an I/O error occurs.
  188.      */
  189.     @Override
  190.     public void write(final char[] cbuf, final int off, final int len) throws IOException {
  191.         try {
  192.             beforeWrite(len);
  193.             out.write(cbuf, off, len);
  194.             afterWrite(len);
  195.         } catch (final IOException e) {
  196.             handleIOException(e);
  197.         }
  198.     }

  199.     /**
  200.      * Invokes the delegate's {@code write(int)} method.
  201.      * @param c the character to write
  202.      * @throws IOException if an I/O error occurs.
  203.      */
  204.     @Override
  205.     public void write(final int c) throws IOException {
  206.         try {
  207.             beforeWrite(1);
  208.             out.write(c);
  209.             afterWrite(1);
  210.         } catch (final IOException e) {
  211.             handleIOException(e);
  212.         }
  213.     }

  214.     /**
  215.      * Invokes the delegate's {@code write(String)} method.
  216.      * @param str the string to write
  217.      * @throws IOException if an I/O error occurs.
  218.      */
  219.     @Override
  220.     public void write(final String str) throws IOException {
  221.         try {
  222.             final int len = IOUtils.length(str);
  223.             beforeWrite(len);
  224.             out.write(str);
  225.             afterWrite(len);
  226.         } catch (final IOException e) {
  227.             handleIOException(e);
  228.         }
  229.     }

  230.     /**
  231.      * Invokes the delegate's {@code write(String)} method.
  232.      * @param str the string to write
  233.      * @param off The start offset
  234.      * @param len The number of characters to write
  235.      * @throws IOException if an I/O error occurs.
  236.      */
  237.     @Override
  238.     public void write(final String str, final int off, final int len) throws IOException {
  239.         try {
  240.             beforeWrite(len);
  241.             out.write(str, off, len);
  242.             afterWrite(len);
  243.         } catch (final IOException e) {
  244.             handleIOException(e);
  245.         }
  246.     }

  247. }