Coverage Report - com.aragost.javahg.internals.BlockInputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
BlockInputStream
90%
37/41
83%
15/18
2.556
BlockInputStream$InvalidStreamException
100%
1/1
N/A
2.556
 
 1  
 /*
 2  
  * #%L
 3  
  * JavaHg
 4  
  * %%
 5  
  * Copyright (C) 2011 aragost Trifork ag
 6  
  * %%
 7  
  * Permission is hereby granted, free of charge, to any person obtaining a copy
 8  
  * of this software and associated documentation files (the "Software"), to deal
 9  
  * in the Software without restriction, including without limitation the rights
 10  
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  
  * copies of the Software, and to permit persons to whom the Software is
 12  
  * furnished to do so, subject to the following conditions:
 13  
  * 
 14  
  * The above copyright notice and this permission notice shall be included in
 15  
  * all copies or substantial portions of the Software.
 16  
  * 
 17  
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  
  * THE SOFTWARE.
 24  
  * #L%
 25  
  */
 26  
 package com.aragost.javahg.internals;
 27  
 
 28  
 import java.io.IOException;
 29  
 import java.io.InputStream;
 30  
 
 31  
 import com.aragost.javahg.log.Logger;
 32  
 import com.aragost.javahg.log.LoggerFactory;
 33  
 
 34  
 /**
 35  
  * An input stream reading one channel block.
 36  
  */
 37  
 public class BlockInputStream extends InputStream {
 38  
 
 39  1
     private static final Logger LOG = LoggerFactory.getLogger(BlockInputStream.class);
 40  
 
 41  1
     public static final BlockInputStream EMPTY = new BlockInputStream();
 42  
 
 43  
     private final InputStream stream;
 44  
 
 45  
     /**
 46  
      * Name of channel
 47  
      */
 48  
     private final char channel;
 49  
 
 50  
     /**
 51  
      * Length of the block
 52  
      */
 53  
     private final int length;
 54  
 
 55  
     /**
 56  
      * Number of bytes left in the block
 57  
      */
 58  
     private int countDown;
 59  
 
 60  1
     private BlockInputStream() {
 61  1
         this.stream = null;
 62  1
         this.channel = 'o';
 63  1
         this.countDown = this.length = 0;
 64  1
     }
 65  
 
 66  
     /**
 67  
      * Construct a new BlockInputStream. This will read the channel (1
 68  
      * byte) and length (4 bytes) from the argument InputStream.
 69  
      * 
 70  
      * @param stream
 71  
      * @throws IOException
 72  
      *             from reading channel and length from the underlying
 73  
      *             stream
 74  
      */
 75  8985
     BlockInputStream(InputStream stream) throws IOException {
 76  8985
         this.stream = stream;
 77  8985
         this.channel = (char) stream.read();
 78  8974
         this.length = this.countDown = readInt();
 79  8974
         if (this.countDown == -1) {
 80  16
             throw new InvalidStreamException();
 81  
         }
 82  8958
         if (Character.isUpperCase(this.channel)) {
 83  
             // Uppercase means mandatory, which is assumed to be an
 84  
             // "input" channel
 85  5
             this.countDown = 0;
 86  
         }
 87  8958
     }
 88  
 
 89  
     @Override
 90  
     public int read() throws IOException {
 91  1
         if (countDown == 0) {
 92  0
             return -1;
 93  
         } else {
 94  1
             this.countDown--;
 95  1
             int read = this.stream.read();
 96  0
             LOG.debug("stdout char: {}", (char) read);
 97  0
             return read;
 98  
         }
 99  
     }
 100  
 
 101  
     @Override
 102  
     public int read(byte[] b, int off, int len) throws IOException {
 103  11672
         if (this.countDown == 0) {
 104  5160
             return -1;
 105  
         }
 106  6512
         if (len > this.countDown) {
 107  5164
             len = this.countDown;
 108  
         }
 109  6512
         int result = stream.read(b, off, len);
 110  6512
         if (result == -1) {
 111  
             // The server should provide enough bytes. It told the
 112  
             // length up front
 113  
             // This can possible happen if the server is killed. Throw
 114  
             // an IOException so it is properly handled by the
 115  
             // Server#verifyServerProcess method
 116  2
             throw new IOException("Unexpected EOF");
 117  
         }
 118  6510
         this.countDown -= result;
 119  6510
         if (LOG.isDebugEnabled()) {
 120  0
             LOG.debug("read {}: '{}'", "" + this.channel + result, new String(b, off, result));
 121  
         }
 122  6510
         return result;
 123  
     }
 124  
 
 125  
     @Override
 126  
     public int available() throws IOException {
 127  5794
         if (this.countDown == 0) {
 128  5790
             return 0;
 129  
         }
 130  4
         int underlyingAvailable = this.stream.available();
 131  4
         return underlyingAvailable > this.countDown ? this.countDown : underlyingAvailable;
 132  
     }
 133  
 
 134  
     /**
 135  
      * @return int value (big endian) of the next 4 bytes from the
 136  
      *         underlying stream
 137  
      * @throws IOException
 138  
      */
 139  
     int readInt() throws IOException {
 140  12763
         return Utils.readBigEndian(this.stream);
 141  
     }
 142  
 
 143  
     char getChannel() {
 144  8756
         return this.channel;
 145  
     }
 146  
 
 147  
     int getLength() {
 148  3792
         return length;
 149  
     }
 150  
 
 151  
     int getBytesLeft() {
 152  8377
         return this.countDown;
 153  
     }
 154  
 
 155  
     /**
 156  
      * Exception to indicate that channel and length could not be read
 157  
      * from the underlying stream.
 158  
      */
 159  16
     public static class InvalidStreamException extends RuntimeException {
 160  
 
 161  
         private static final long serialVersionUID = 1L;
 162  
 
 163  
     }
 164  
 
 165  
 }