jtulach@61: package org.apidesign.delegatingwriter; jtulach@61: jtulach@61: import java.io.BufferedWriter; jtulach@61: import java.io.IOException; jtulach@61: import java.io.Writer; jtulach@61: jtulach@61: /** jtulach@61: * This is a regular {@link BufferedWriter}, just its implementation jtulach@61: * of the append method can choose from three options. This allows us to jtulach@61: * simulate the potential pros and cons of various possible implementations. jtulach@61: * jtulach@61: * @author Jaroslav Tulach jtulach@61: */ jtulach@61: public class AltBufferedWriter extends BufferedWriter { jtulach@61: private final Writer out; jtulach@61: private final Behaviour behaviour; jtulach@61: jtulach@63: public AltBufferedWriter(Writer out) { jtulach@63: // behave exactly like BufferedWriter in 1.5 behaves jtulach@63: this(out, Behaviour.DELEGATE_TO_SUPER); jtulach@63: } jtulach@61: public AltBufferedWriter(Writer out, Behaviour behaviour) { jtulach@61: super(out); jtulach@61: this.out = out; jtulach@61: this.behaviour = behaviour; jtulach@61: } jtulach@61: jtulach@61: @Override jtulach@61: public Writer append(CharSequence csq) throws IOException { jtulach@61: switch (behaviour) { jtulach@154: case THROW_EXCEPTION: jtulach@154: return appendThrowException(csq); jtulach@154: case DELEGATE_TO_SUPER: jtulach@154: return appendDelegateToSuper(csq); jtulach@154: case DELEGATE_TO_OUT: jtulach@154: return appendDelegateToUnderlaying(csq); jtulach@154: case DELEGATE_CONDITIONALLY: jtulach@154: return appendDelegateConditionally(csq); jtulach@154: default: jtulach@154: throw new IllegalStateException("Unknown" + behaviour); jtulach@61: } jtulach@61: } jtulach@61: jtulach@61: public Writer appendThrowException(CharSequence csq) throws IOException { jtulach@154: /* in case of real code, this would be part of jtulach@154: the regular append method. BEGIN: writer.throw jtulach@63: public Writer append(CharSequence csq) throws IOException { jtulach@154: /* thrown an exception as this method is new and jtulach@154: subclasses need to override it */ jtulach@61: throw new UnsupportedOperationException(); jtulach@61: } jtulach@61: // END: writer.throw jtulach@61: jtulach@61: public Writer appendDelegateToSuper(CharSequence csq) throws IOException { jtulach@61: // non-efficient variant of delegating via converting to String first jtulach@61: // and using one of methods that existed in 1.4 jtulach@69: // BEGIN: writer.super jtulach@61: if (csq == null) { jtulach@61: write("null"); jtulach@61: } else { jtulach@61: write(csq.toString()); jtulach@61: } jtulach@61: return this; jtulach@69: // END: writer.super jtulach@61: } jtulach@61: jtulach@61: public Writer appendDelegateToUnderlaying(CharSequence csq) throws IOException { jtulach@61: // BEGIN: writer.delegateout jtulach@63: // efficient, yet dangerous delegation skipping methods unknown to jtulach@61: // subclasses that used version 1.4 jtulach@210: if (shouldBufferAsTheSequenceIsNotTooBig(csq)) { jtulach@67: write(csq.toString()); jtulach@67: } else { jtulach@67: flush(); jtulach@67: out.append(csq); jtulach@67: } jtulach@61: return this; jtulach@61: // END: writer.delegateout jtulach@61: } jtulach@61: jtulach@154: private Writer appendDelegateConditionally(CharSequence csq) jtulach@154: throws IOException { jtulach@64: // BEGIN: writer.conditionally jtulach@64: boolean isOverriden = false; jtulach@64: try { jtulach@64: isOverriden = jtulach@154: ( jtulach@154: getClass().getMethod( jtulach@154: "write", String.class jtulach@154: ).getDeclaringClass() != Writer.class jtulach@154: ) || jtulach@154: ( jtulach@154: getClass().getMethod( jtulach@154: "write", Integer.TYPE jtulach@154: ).getDeclaringClass() != BufferedWriter.class jtulach@154: ) || jtulach@154: ( jtulach@154: getClass().getMethod( jtulach@154: "write", String.class, Integer.TYPE, Integer.TYPE jtulach@154: ).getDeclaringClass() != BufferedWriter.class jtulach@154: ); jtulach@64: } catch (Exception ex) { jtulach@64: throw new IOException(ex); jtulach@64: } jtulach@64: jtulach@210: if (isOverriden || shouldBufferAsTheSequenceIsNotTooBig(csq)) { jtulach@67: write(csq.toString()); jtulach@64: } else { jtulach@67: flush(); jtulach@64: out.append(csq); jtulach@64: } jtulach@64: return this; jtulach@64: // END: writer.conditionally jtulach@61: } jtulach@210: jtulach@210: /** At the end the purpose of BufferedWriter is to buffer writes, this jtulach@210: * method is here to decide when it is OK to prefer buffering and when jtulach@210: * it is better to delegate directly into the underlaying stream. jtulach@210: * jtulach@210: * @param csq the seqence to evaluate jtulach@210: * @return true if buffering from super class should be used jtulach@210: */ jtulach@210: private static boolean shouldBufferAsTheSequenceIsNotTooBig(CharSequence csq) { jtulach@210: if (csq == null) { jtulach@210: return false; jtulach@210: } jtulach@210: // as buffers are usually bigger than 1024, it makes sense to jtulach@210: // pay the penalty of converting the sequence to string, but buffering jtulach@210: // the write jtulach@210: if (csq.length() < 1024) { jtulach@210: return true; jtulach@210: } else { jtulach@210: // otherwise, just directly write down the char sequence jtulach@210: return false; jtulach@210: } jtulach@210: } jtulach@61: jtulach@61: public enum Behaviour { jtulach@154: THROW_EXCEPTION, jtulach@154: DELEGATE_TO_SUPER, jtulach@154: DELEGATE_TO_OUT, jtulach@154: DELEGATE_CONDITIONALLY jtulach@61: } jtulach@61: }