View Javadoc
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  final class OctaveExecuteReader extends Reader {
34  
35      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      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      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      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  			final String spacer) {
87          this.octaveReader = octaveReader;
88          this.spacer = spacer;
89      }
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         if (this.eof) {
116             return -1;
117         }
118         if (this.buffer == null) {
119             final String line = this.octaveReader.readLine();
120             if (LOG.isTraceEnabled()) {
121                 LOG.trace("octaveReader.readLine() = " + 
122 			  StringUtil.jQuote(line));
123             }
124             if (line == null) {
125                 throw new IOException("Pipe to octave-process broken");
126             }
127             if (this.spacer.equals(line)) {
128                 this.eof = true;
129                 return -1;
130             }
131 
132 	    // line possibly preceeded by \n 
133             this.buffer = new StringBuffer(line.length() + 1);
134             if (this.firstLine) {
135                 this.firstLine = false;
136             } else {
137                 this.buffer.append('\n');
138             }
139             this.buffer.append(line);
140         }
141 	assert this.buffer != null;
142 
143         final int charsRead = Math.min(this.buffer.length(), len);
144         this.buffer.getChars(0, charsRead, cbuf, off);
145         if (charsRead == buffer.length()) {
146             this.buffer = null;
147         } else {
148             this.buffer.delete(0, charsRead);
149         }
150         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         final char[] buffer1 = new char[4096];
158         // Slurp the rest of the wrapped input
159         while (read(buffer1) != -1) { // NOPMD 
160             // Do nothing
161         }
162         if (this.octaveReader.ready()) {
163             throw new IOException("octaveReader is ready()");
164         }
165         LOG.debug("Reader closed()");
166     }
167 
168 }