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