comparison src/main/java/org/monetdb/mcl/io/BufferedMCLReader.java @ 700:940e266eeccd

Refactor BufferedMCLReader It used to inherit from BufferedReader but there is no reason for that. Also, it used to have a method readLine() which - returned the line read - stored the linetype In the new setup we have a method advance() which reads a line and stores both it and its type. This makes the code more regular and makes it possible to peek ahead without consuming.
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Thu, 08 Dec 2022 15:59:17 +0100 (2022-12-08)
parents 2ded26c5a679
children b4e968e5bd74
comparison
equal deleted inserted replaced
699:0ff364f569a1 700:940e266eeccd
38 * 38 *
39 * @author Fabian Groffen 39 * @author Fabian Groffen
40 * @see org.monetdb.mcl.net.MapiSocket 40 * @see org.monetdb.mcl.net.MapiSocket
41 * @see org.monetdb.mcl.io.BufferedMCLWriter 41 * @see org.monetdb.mcl.io.BufferedMCLWriter
42 */ 42 */
43 public final class BufferedMCLReader extends BufferedReader { 43 public final class BufferedMCLReader /* extends BufferedReader */ {
44 44
45 /** The type of the last line read */ 45 private final BufferedReader inner;
46 private String current = null;
46 private LineType lineType = LineType.UNKNOWN; 47 private LineType lineType = LineType.UNKNOWN;
47 48
48 /** 49 /**
49 * Create a buffering character-input stream that uses a 50 * Create a buffering character-input stream that uses a
50 * default-sized input buffer. 51 * default-sized input buffer.
51 * 52 *
52 * @param in A Reader 53 * @param in A Reader
53 */ 54 */
54 public BufferedMCLReader(final Reader in) { 55 public BufferedMCLReader(final Reader in) {
55 super(in); 56 inner = new BufferedReader(in);
56 } 57 }
57 58
58 /** 59 /**
59 * Create a buffering character-input stream that uses a 60 * Create a buffering character-input stream that uses a
60 * default-sized input buffer, from an InputStream. 61 * default-sized input buffer, from an InputStream.
64 * @throws UnsupportedEncodingException If encoding is not supported 65 * @throws UnsupportedEncodingException If encoding is not supported
65 */ 66 */
66 public BufferedMCLReader(final InputStream in, final String enc) 67 public BufferedMCLReader(final InputStream in, final String enc)
67 throws UnsupportedEncodingException 68 throws UnsupportedEncodingException
68 { 69 {
69 super(new java.io.InputStreamReader(in, enc)); 70 this(new java.io.InputStreamReader(in, enc));
71 }
72
73 public void advance() throws IOException {
74 if (lineType == LineType.PROMPT)
75 return;
76
77 current = inner.readLine();
78 lineType = LineType.classify(current);
79 if (lineType == LineType.ERROR && current != null && !current.matches("^![0-9A-Z]{5}!.+")) {
80 current = "!22000!" + current.substring(1);
81 }
70 } 82 }
71 83
72 /** 84 /**
73 * Read a line of text. A line is considered to be terminated by 85 * Resets the linetype to UNKNOWN.
74 * any one of a line feed ('\n'), a carriage return ('\r'), or a
75 * carriage return followed immediately by a linefeed. Before this
76 * method returns, it sets the linetype to any of the in MCL
77 * recognised line types.
78 *
79 * Warning: until the server properly prefixes all of its error
80 * messages with SQLSTATE codes, this method prefixes all errors it
81 * sees without sqlstate with the generic data exception code (22000).
82 *
83 * @return A String containing the contents of the line, not
84 * including any line-termination characters, or null if the
85 * end of the stream has been reached
86 * @throws IOException If an I/O error occurs
87 */ 86 */
88 @Override 87 public void resetLineType() {
89 public String readLine() throws IOException { 88 lineType = LineType.UNKNOWN;
90 String r = super.readLine();
91 setLineType(r);
92 if (lineType == LineType.ERROR && r != null && !r.matches("^![0-9A-Z]{5}!.+")) {
93 r = "!22000!" + r.substring(1);
94 }
95 return r;
96 } 89 }
97 90
98 /** 91 /**
99 * Sets the linetype to the type of the string given. If the string 92 * Return the current line, or null if we're at the end or before the beginning.
100 * is null, lineType is set to UNKNOWN. 93 * @return the current line or null
101 *
102 * @param line the string to examine
103 */ 94 */
104 public void setLineType(final String line) { 95 public String getLine() {
105 lineType = LineType.classify(line); 96 return current;
106 } 97 }
107 98
108 /** 99 /**
109 * getLineType returns the type of the last line read. 100 * getLineType returns the type of the current line.
110 * 101 *
111 * @return Linetype representing the kind of line this is, one of the 102 * @return Linetype representing the kind of line this is, one of the
112 * following enums: UNKNOWN, HEADER, ERROR, RESULT, 103 * following enums: UNKNOWN, HEADER, ERROR, RESULT,
113 * PROMPT, MORE, FILETRANSFER, SOHEADER, REDIRECT, INFO 104 * PROMPT, MORE, FILETRANSFER, SOHEADER, REDIRECT, INFO
114 */ 105 */
115 public LineType getLineType() { 106 public LineType getLineType() {
116 return lineType; 107 return lineType;
117 } 108 }
118 109
119 /** 110 /**
120 * Reads up till the MonetDB prompt, indicating the server is ready 111 * Discard the remainder of the response but collect any further error messages.
121 * for a new command. All read data is discarded. If the last line
122 * read by readLine() was a prompt, this method will immediately return.
123 *
124 * If there are errors present in the lines that are read, then they
125 * are put in one string and returned <b>after</b> the prompt has
126 * been found. If no errors are present, null will be returned.
127 * 112 *
128 * @return a string containing error messages, or null if there aren't any 113 * @return a string containing error messages, or null if there aren't any
129 * @throws IOException if an IO exception occurs while talking to the server 114 * @throws IOException if an IO exception occurs while talking to the server
130 * 115 *
131 * TODO(Wouter): should probably not have to be synchronized. 116 * TODO(Wouter): should probably not have to be synchronized.
132 */ 117 */
133 final public synchronized String waitForPrompt() throws IOException {
134 StringBuilder errmsgs = null;
135 String tmp;
136 118
119
120 final public synchronized String discardRemainder() throws IOException {
121 return discard(null);
122 }
123
124 final public synchronized String discardRemainder(String error) throws IOException {
125 final StringBuilder sb;
126
127 if (error != null) {
128 sb = makeErrorBuffer();
129 sb.append(error);
130 } else {
131 sb = null;
132 }
133 return discard(sb);
134 }
135
136 final synchronized String discard(StringBuilder errmsgs) throws IOException {
137 while (lineType != LineType.PROMPT) { 137 while (lineType != LineType.PROMPT) {
138 tmp = readLine(); 138 advance();
139 if (tmp == null) 139 if (getLine() == null)
140 throw new IOException("Connection to server lost!"); 140 throw new IOException("Connection to server lost!");
141 if (lineType == LineType.ERROR) { 141 if (getLineType() == LineType.ERROR) {
142 if (errmsgs == null) 142 if (errmsgs == null)
143 errmsgs = new StringBuilder(128); 143 errmsgs = new StringBuilder(128);
144 errmsgs.append('\n').append(tmp.substring(1)); 144 errmsgs.append('\n').append(getLine().substring(1));
145 } 145 }
146 } 146 }
147 if (errmsgs == null) 147 if (errmsgs == null)
148 return null; 148 return null;
149 return errmsgs.toString().trim(); 149 return errmsgs.toString().trim();
150 } 150 }
151
152 private final StringBuilder makeErrorBuffer() {
153 return new StringBuilder(128);
154 }
155
156 public void close() throws IOException {
157 inner.close();
158 }
151 } 159 }