Coverage Report - com.aragost.javahg.internals.PatternReplacingOutputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
PatternReplacingOutputStream
90%
28/31
86%
13/15
5.5
PatternReplacingOutputStream$1
100%
1/1
N/A
5.5
PatternReplacingOutputStream$State
100%
5/5
N/A
5.5
 
 1  
 package com.aragost.javahg.internals;
 2  
 
 3  
 import java.io.ByteArrayOutputStream;
 4  
 import java.io.FilterOutputStream;
 5  
 import java.io.IOException;
 6  
 import java.io.OutputStream;
 7  
 import java.util.Map;
 8  
 
 9  
 public class PatternReplacingOutputStream extends FilterOutputStream {
 10  
 
 11  6
     private enum State {
 12  
         /**
 13  
          * The normal state where bytes are directly written though to
 14  
          * the underlying stream
 15  
          */
 16  1
         NORMAL,
 17  
         /**
 18  
          * A '%' has just been seen, so potentially a named pattern
 19  
          * will start
 20  
          */
 21  1
         MAYBE_NAME_START,
 22  
         /**
 23  
          * Bytes written to the stream is part of a named pattern,
 24  
          * i.o. recently '%{' was written to the stream
 25  
          */
 26  1
         READING_NAME,
 27  
         /**
 28  
          * 
 29  
          */
 30  1
         WRITING_REPLACEMENT;
 31  
     };
 32  
 
 33  7
     private final ByteArrayOutputStream nameBuffer = new ByteArrayOutputStream(20);
 34  
 
 35  
     private final Map<String, byte[]> replacements;
 36  
 
 37  7
     private State state = State.NORMAL;
 38  
 
 39  
     public PatternReplacingOutputStream(OutputStream out, Map<String, byte[]> replacements) {
 40  7
         super(out);
 41  7
         this.replacements = replacements;
 42  7
     }
 43  
 
 44  
     @Override
 45  
     public synchronized void write(int b) throws IOException {
 46  1
         switch (this.state) {
 47  
         case NORMAL:
 48  348
             if (b == '%') {
 49  11
                 this.state = State.MAYBE_NAME_START;
 50  
             } else {
 51  337
                 super.write(b);
 52  
             }
 53  337
             break;
 54  
         case MAYBE_NAME_START:
 55  11
             if (b == '{') {
 56  10
                 this.state = State.READING_NAME;
 57  
             } else {
 58  1
                 super.write('%');
 59  1
                 if (b == '%') {
 60  1
                     this.state = State.NORMAL;
 61  
                 } else {
 62  0
                     super.write(b);
 63  
                 }
 64  
             }
 65  0
             break;
 66  
         case READING_NAME:
 67  61
             if (b == '}') {
 68  9
                 this.state = State.WRITING_REPLACEMENT;
 69  9
                 String name = new String(this.nameBuffer.toByteArray()); 
 70  9
                 this.nameBuffer.reset();
 71  9
                 byte[] replacement = this.replacements.get(name);
 72  9
                 if (replacement == null) {
 73  1
                     replacement = ("%{" + name + "=null}").getBytes();
 74  
                 }
 75  9
                 this.out.write(replacement);
 76  9
                 this.state = State.NORMAL;
 77  9
             } else {
 78  52
                 this.nameBuffer.write(b);
 79  
             }
 80  52
             break;
 81  
         case WRITING_REPLACEMENT:
 82  0
             super.write(b);
 83  
             break;
 84  
         }
 85  420
     }
 86  
 }