samples/delegatingwriter/src/org/apidesign/delegatingwriter/AltBufferedWriter.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 30 Oct 2014 21:30:10 +0100
changeset 409 40cabcdcd2be
parent 209 1c999569643b
permissions -rw-r--r--
Updating to NBMs from NetBeans 8.0.1 as some of them are required to run on JDK8
     1 package org.apidesign.delegatingwriter;
     2 
     3 import java.io.BufferedWriter;
     4 import java.io.IOException;
     5 import java.io.Writer;
     6 
     7 /**
     8  * This is a regular {@link BufferedWriter}, just its implementation
     9  * of the append method can choose from three options. This allows us to
    10  * simulate the potential pros and cons of various possible implementations.
    11  * 
    12  * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    13  */
    14 public class AltBufferedWriter extends BufferedWriter {
    15     private final Writer out;
    16     private final Behaviour behaviour;
    17     
    18     public AltBufferedWriter(Writer out) {
    19         // behave exactly like BufferedWriter in 1.5 behaves
    20         this(out, Behaviour.DELEGATE_TO_SUPER);
    21     }
    22     public AltBufferedWriter(Writer out, Behaviour behaviour) {
    23         super(out);
    24         this.out = out;
    25         this.behaviour = behaviour;
    26     }
    27     
    28     @Override
    29     public Writer append(CharSequence csq) throws IOException {
    30         switch (behaviour) {
    31             case THROW_EXCEPTION: 
    32                 return appendThrowException(csq); 
    33             case DELEGATE_TO_SUPER: 
    34                 return appendDelegateToSuper(csq);
    35             case DELEGATE_TO_OUT: 
    36                 return appendDelegateToUnderlaying(csq);
    37             case DELEGATE_CONDITIONALLY: 
    38                 return appendDelegateConditionally(csq);
    39             default: 
    40                 throw new IllegalStateException("Unknown" + behaviour);
    41         }
    42     }
    43     
    44     public Writer appendThrowException(CharSequence csq) throws IOException {
    45         /* in case of real code, this would be part of 
    46          the regular append method. BEGIN: writer.throw
    47     public Writer append(CharSequence csq) throws IOException {
    48         /* thrown an exception as this method is new and 
    49          subclasses need to override it */
    50         throw new UnsupportedOperationException();
    51     }
    52     // END: writer.throw
    53     
    54     public Writer appendDelegateToSuper(CharSequence csq) throws IOException {
    55         // non-efficient variant of delegating via converting to String first 
    56         // and using one of methods that existed in 1.4
    57         // BEGIN: writer.super
    58         if (csq == null) {
    59             write("null");
    60         } else {
    61             write(csq.toString());
    62         }
    63         return this;
    64         // END: writer.super
    65     }
    66     
    67     public Writer appendDelegateToUnderlaying(CharSequence csq) throws IOException {
    68         // BEGIN: writer.delegateout
    69         // efficient, yet dangerous delegation skipping methods unknown to 
    70         // subclasses that used version 1.4
    71         if (shouldBufferAsTheSequenceIsNotTooBig(csq)) {
    72             write(csq.toString());
    73         } else {
    74             flush();
    75             out.append(csq);
    76         }
    77         return this;
    78         // END: writer.delegateout
    79     }
    80 
    81     private Writer appendDelegateConditionally(CharSequence csq) 
    82     throws IOException {
    83         // BEGIN: writer.conditionally
    84         boolean isOverriden = false;
    85         try {
    86             isOverriden = 
    87                 (
    88                     getClass().getMethod(
    89                         "write", String.class
    90                     ).getDeclaringClass() != Writer.class
    91                 ) ||
    92                 (
    93                     getClass().getMethod(
    94                         "write", Integer.TYPE
    95                     ).getDeclaringClass() != BufferedWriter.class
    96                 ) ||
    97                 (
    98                     getClass().getMethod(
    99                         "write", String.class, Integer.TYPE, Integer.TYPE
   100                     ).getDeclaringClass() != BufferedWriter.class
   101                 );
   102         } catch (Exception ex) {
   103             throw new IOException(ex);
   104         }
   105         
   106         if (isOverriden || shouldBufferAsTheSequenceIsNotTooBig(csq)) {
   107             write(csq.toString());
   108         } else {
   109             flush();
   110             out.append(csq);
   111         }
   112         return this;
   113         // END: writer.conditionally
   114     }
   115 
   116     /** At the end the purpose of BufferedWriter is to buffer writes, this
   117      * method is here to decide when it is OK to prefer buffering and when 
   118      * it is better to delegate directly into the underlaying stream.
   119      * 
   120      * @param csq the seqence to evaluate
   121      * @return true if buffering from super class should be used
   122      */
   123     private static boolean shouldBufferAsTheSequenceIsNotTooBig(CharSequence csq) {
   124         if (csq == null) {
   125             return false;
   126         }
   127         // as buffers are usually bigger than 1024, it makes sense to 
   128         // pay the penalty of converting the sequence to string, but buffering
   129         // the write
   130         if (csq.length() < 1024) {
   131             return true;
   132         } else {
   133             // otherwise, just directly write down the char sequence
   134             return false;
   135         }
   136     }
   137     
   138     public enum Behaviour {
   139         THROW_EXCEPTION, 
   140         DELEGATE_TO_SUPER, 
   141         DELEGATE_TO_OUT, 
   142         DELEGATE_CONDITIONALLY
   143     }
   144 }