Mercurial > hg > monetdb-java
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 <- 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 & 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 } |