comparison src/main/java/nl/cwi/monetdb/mcl/io/BufferedMCLReader.java @ 0:a5a898f6886c

Copy of MonetDB java directory changeset e6e32756ad31.
author Sjoerd Mullender <sjoerd@acm.org>
date Wed, 21 Sep 2016 09:34:48 +0200 (2016-09-21)
parents
children e605cdd6373f b9b35ca2eec2
comparison
equal deleted inserted replaced
-1:000000000000 0:a5a898f6886c
1 /*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2016 MonetDB B.V.
7 */
8
9 package nl.cwi.monetdb.mcl.io;
10
11 import java.io.BufferedReader;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.InputStreamReader;
15 import java.io.Reader;
16 import java.io.UnsupportedEncodingException;
17
18 /**
19 * Read text from a character-input stream, buffering characters so as
20 * to provide a means for efficient reading of characters, arrays and
21 * lines. This class is based on the BufferedReader class, and provides
22 * extra functionality useful for MCL.
23 *
24 * The BufferedMCLReader is typically used as layer inbetween an
25 * InputStream and a specific interpreter of the data.
26 * <pre>
27 * / Response
28 * BufferedMCLReader ---o &lt;- Tuple
29 * \ DataBlock
30 * </pre>
31 * Because the BufferedMCLReader provides an efficient way to access the
32 * data from the stream in a linewise fashion, whereby each line is
33 * identified as a certain type, consumers can easily decide how to
34 * parse each retrieved line. The line parsers from
35 * nl.cwi.monetdb.mcl.parser are well suited to work with the lines
36 * outputted by the BufferedMCLReader.
37 * This class is client-oriented, as it doesn't take into account the
38 * messages as the server receives them.
39 *
40 * @author Fabian Groffen <Fabian.Groffen>
41 * @see nl.cwi.monetdb.mcl.net.MapiSocket
42 * @see nl.cwi.monetdb.mcl.io.BufferedMCLWriter
43 */
44 public class BufferedMCLReader extends BufferedReader {
45 /** The type of the last line read */
46 private int lineType;
47
48 /** "there is currently no line", or the the type is unknown is
49 represented by UNKNOWN */
50 public final static int UNKNOWN = 0;
51 /** a line starting with ! indicates ERROR */
52 public final static int ERROR = '!';
53 /** a line starting with % indicates HEADER */
54 public final static int HEADER = '%';
55 /** a line starting with [ indicates RESULT */
56 public final static int RESULT = '[';
57 /** a line which matches the pattern of prompt1 is a PROMPT */
58 public final static int PROMPT = '.';
59 /** a line which matches the pattern of prompt2 is a MORE */
60 public final static int MORE = ',';
61 /** a line starting with &amp; indicates the start of a header block */
62 public final static int SOHEADER = '&';
63 /** a line starting with ^ indicates REDIRECT */
64 public final static int REDIRECT = '^';
65 /** a line starting with # indicates INFO */
66 public final static int INFO = '#';
67
68 /**
69 * Create a buffering character-input stream that uses a
70 * default-sized input buffer.
71 *
72 * @param in A Reader
73 */
74 public BufferedMCLReader(Reader in) {
75 super(in);
76 }
77
78 /**
79 * Create a buffering character-input stream that uses a
80 * default-sized input buffer, from an InputStream.
81 *
82 * @param in An InputStream
83 * @param enc Encoding
84 */
85 public BufferedMCLReader(InputStream in, String enc)
86 throws UnsupportedEncodingException
87 {
88 super(new InputStreamReader(in, enc));
89 }
90
91 /**
92 * Read a line of text. A line is considered to be terminated by
93 * any one of a line feed ('\n'), a carriage return ('\r'), or a
94 * carriage return followed immediately by a linefeed. Before this
95 * method returns, it sets the linetype to any of the in MCL
96 * recognised line types.
97 *
98 * Warning: until the server properly prefixes all of its error
99 * messages with SQLSTATE codes, this method prefixes all errors it
100 * sees without sqlstate with the generic data exception code
101 * (22000).
102 *
103 * @return A String containing the contents of the line, not
104 * including any line-termination characters, or null if the
105 * end of the stream has been reached
106 * @throws IOException If an I/O error occurs
107 */
108 @Override
109 public String readLine() throws IOException {
110 String r = super.readLine();
111 setLineType(r);
112 if (lineType == ERROR && !r.matches("^![0-9A-Z]{5}!.+"))
113 r = "!22000!" + r.substring(1);
114 return r;
115 }
116
117 /**
118 * Sets the linetype to the type of the string given. If the string
119 * is null, lineType is set to UNKNOWN.
120 *
121 * @param line the string to examine
122 */
123 void setLineType(String line) {
124 lineType = UNKNOWN;
125 if (line == null || line.length() == 0)
126 return;
127 switch (line.charAt(0)) {
128 case '!':
129 lineType = ERROR;
130 break;
131 case '&':
132 lineType = SOHEADER;
133 break;
134 case '%':
135 lineType = HEADER;
136 break;
137 case '[':
138 lineType = RESULT;
139 break;
140 case '=':
141 lineType = RESULT;
142 break;
143 case '^':
144 lineType = REDIRECT;
145 break;
146 case '#':
147 lineType = INFO;
148 break;
149 case '.':
150 lineType = PROMPT;
151 break;
152 case ',':
153 lineType = MORE;
154 break;
155 }
156 }
157
158 /**
159 * getLineType returns the type of the last line read.
160 *
161 * @return an integer representing the kind of line this is, one of the
162 * following constants: UNKNOWN, HEADER, ERROR, PROMPT,
163 * RESULT, REDIRECT, INFO
164 */
165 public int getLineType() {
166 return lineType;
167 }
168
169 /**
170 * Reads up till the MonetDB prompt, indicating the server is ready
171 * for a new command. All read data is discarded. If the last line
172 * read by readLine() was a prompt, this method will immediately
173 * return.
174 *
175 * If there are errors present in the lines that are read, then they
176 * are put in one string and returned <b>after</b> the prompt has
177 * been found. If no errors are present, null will be returned.
178 *
179 * @return a string containing error messages, or null if there aren't any
180 * @throws IOException if an IO exception occurs while talking to the server
181 *
182 * TODO(Wouter): should probably not have to be synchronized. + StringBuilder...
183 */
184 final public synchronized String waitForPrompt() throws IOException {
185 String ret = "", tmp;
186 while (lineType != PROMPT) {
187 if ((tmp = readLine()) == null)
188 throw new IOException("Connection to server lost!");
189 if (lineType == ERROR)
190 ret += "\n" + tmp.substring(1);
191 }
192 return ret == "" ? null : ret.trim();
193 }
194
195 }