Coverage Report - eu.simuline.octave.exec.OctaveExecuteReader
 
Classes in this File Line Coverage Branch Coverage Complexity
OctaveExecuteReader
89%
35/39
72%
16/22
5.333
 
 1  
 /*
 2  
  * Copyright 2007, 2008, 2009 Ange Optimization ApS
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package eu.simuline.octave.exec;
 17  
 
 18  
 import java.io.BufferedReader;
 19  
 import java.io.IOException;
 20  
 import java.io.Reader;
 21  
 
 22  
 import org.apache.commons.logging.Log;
 23  
 import org.apache.commons.logging.LogFactory;
 24  
 
 25  
 import eu.simuline.octave.util.StringUtil;
 26  
 
 27  
 /**
 28  
  * Reader that passes the reading on to the output from the octave process 
 29  
  * until the spacer reached, then it returns EOF. 
 30  
  * When this reader is closed 
 31  
  * the underlying reader is slurped up to the spacer. 
 32  
  */
 33  2
 final class OctaveExecuteReader extends Reader {
 34  
 
 35  2
     private static final Log LOG = LogFactory.getLog(OctaveExecuteReader.class);
 36  
 
 37  
     /**
 38  
      * The wrapped reader. 
 39  
      */
 40  
     private final BufferedReader octaveReader;
 41  
 
 42  
     /**
 43  
      * The string signifying end of stream. 
 44  
      */
 45  
     private final String spacer;
 46  
 
 47  
     /**
 48  
      * The current line read from {@link #octaveReader} 
 49  
      * but not yet passed to a char-array by {@link #read(char[], int, int)}. 
 50  
      * If this buffer were empty, it is <code>null</code> instead, 
 51  
      * which is also the initial value. 
 52  
      * If this is not the first line, the line read from {@link #octaveReader} 
 53  
      * is preceeded by newline before being passed to {@link #buffer}. 
 54  
      */
 55  2142
     private StringBuffer buffer = null;
 56  
 
 57  
     /**
 58  
      * Whether reading the first line. 
 59  
      * Initially, this is true. 
 60  
      * It is set to false, by {@link #read(char[], int, int)} 
 61  
      * if {@link #buffer} is filled for the first time. 
 62  
      */
 63  2142
     private boolean firstLine = true;
 64  
 
 65  
     /**
 66  
      * Whether end of reader found. 
 67  
      * Initially, this is false. 
 68  
      * It is set to false, by {@link #read(char[], int, int)} 
 69  
      * if {@link #buffer} equals {@link #spacer}, 
 70  
      * not really end of {@link #octaveReader}. 
 71  
      */
 72  2142
     private boolean eof = false;
 73  
 
 74  
     /**
 75  
      * This reader will read from <code>octaveReader</code> 
 76  
      * until a single line equal() <code>spacer</code> is read, 
 77  
      * after that this reader will return eof. 
 78  
      * When this reader is closed it will update the state of octave to NONE. 
 79  
      *
 80  
      * @param octaveReader
 81  
      *    the wrapped reader 
 82  
      * @param spacer
 83  
      *    the line signifying end of stream. 
 84  
      */
 85  
     OctaveExecuteReader(final BufferedReader octaveReader, 
 86  2142
                         final String spacer) {
 87  2142
         this.octaveReader = octaveReader;
 88  2142
         this.spacer = spacer;
 89  2142
     }
 90  
 
 91  
     /**
 92  
      * Reads characters into a portion of an array. 
 93  
      * This method will block until some input is available, 
 94  
      * an I/O error occurs, or the end of the stream is reached.
 95  
      *
 96  
      * @param cbuf 
 97  
      *    Destination buffer
 98  
      * @param off 
 99  
      *    Offset at which to start storing characters. 
 100  
      * @param len 
 101  
      *    Maximum number of characters to read. 
 102  
      * @return
 103  
      *    The number of characters read, 
 104  
      *    or -1 if the end of the stream has been reached. 
 105  
      *    The latter is the case if {@link #eof} is set 
 106  
      *    which means that line {@link #spacer} has been found. 
 107  
      * @throws IOException 
 108  
      *    If an I/O error occurs. 
 109  
      *    This is true in particular, 
 110  
      *    if null-line has been read from {@link #octaveReader}. 
 111  
      */
 112  
     @Override
 113  
     public int read(final char[] cbuf, final int off, final int len) 
 114  
         throws IOException {
 115  18972
         if (this.eof) {
 116  1950
             return -1;
 117  
         }
 118  17022
         if (this.buffer == null) {
 119  17022
             final String line = this.octaveReader.readLine();
 120  17020
             if (LOG.isTraceEnabled()) {
 121  0
                 LOG.trace("octaveReader.readLine() = " + 
 122  0
                           StringUtil.jQuote(line));
 123  
             }
 124  17020
             if (line == null) {
 125  22
                 throw new IOException("Pipe to octave-process broken");
 126  
             }
 127  16998
             if (this.spacer.equals(line)) {
 128  2130
                 this.eof = true;
 129  2130
                 return -1;
 130  
             }
 131  
 
 132  
             // line possibly preceeded by \n 
 133  14868
             this.buffer = new StringBuffer(line.length() + 1);
 134  14868
             if (this.firstLine) {
 135  2130
                 this.firstLine = false;
 136  
             } else {
 137  12738
                 this.buffer.append('\n');
 138  
             }
 139  14868
             this.buffer.append(line);
 140  
         }
 141  14868
         assert this.buffer != null;
 142  
 
 143  14868
         final int charsRead = Math.min(this.buffer.length(), len);
 144  14868
         this.buffer.getChars(0, charsRead, cbuf, off);
 145  14868
         if (charsRead == buffer.length()) {
 146  14868
             this.buffer = null;
 147  
         } else {
 148  0
             this.buffer.delete(0, charsRead);
 149  
         }
 150  14868
         return charsRead;
 151  
     }
 152  
 
 153  
     @Override
 154  
     @SuppressWarnings({"checkstyle:magicnumber", "checkstyle:emptyblock"})
 155  
     // length of buffer is immaterial for function 
 156  
     public void close() throws IOException {
 157  2142
         final char[] buffer1 = new char[4096];
 158  
         // Slurp the rest of the wrapped input
 159  2554
         while (read(buffer1) != -1) { // NOPMD 
 160  
             // Do nothing
 161  
         }
 162  2130
         if (this.octaveReader.ready()) {
 163  0
             throw new IOException("octaveReader is ready()");
 164  
         }
 165  2130
         LOG.debug("Reader closed()");
 166  2130
     }
 167  
 
 168  
 }