Mercurial > hg > monetdb-java
view src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParser.java @ 68:86967be24645 embedded
Ready to start testing the old mapi connection. After passing the tests. The embedded integration will be very straightforward.
author | Pedro Ferreira <pedro.ferreira@monetdbsolutions.com> |
---|---|
date | Wed, 07 Dec 2016 15:59:27 +0100 (2016-12-07) |
parents | 87ba760038b6 |
children | 953422c41194 |
line wrap: on
line source
package nl.cwi.monetdb.mcl.protocol.oldmapi; import nl.cwi.monetdb.jdbc.MonetBlob; import nl.cwi.monetdb.jdbc.MonetClob; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Date; import java.sql.Types; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; /** * Created by ferreira on 12/6/16. */ final class OldMapiTupleLineParser { static int OldMapiParseTupleLine(StringBuilder line, Object[] values, StringBuilder helper, int[] jDBCTypesMap) throws ProtocolException { int len = line.length(); // first detect whether this is a single value line (=) or a real tuple ([) if (line.charAt(0) == '=') { if (values.length != 1) { throw new ProtocolException(values.length + " columns expected, but only single value found"); } // return the whole string but the leading = values[0] = line.substring(1); return 1; } // extract separate fields by examining string, char for char boolean inString = false, escaped = false; int cursor = 2, column = 0, i = 2; for (; i < len; i++) { switch(line.charAt(i)) { default: escaped = false; break; case '\\': escaped = !escaped; break; case '"': /** * If all strings are wrapped between two quotes, a \" can * never exist outside a string. Thus if we believe that we * are not within a string, we can safely assume we're about * to enter a string if we find a quote. * If we are in a string we should stop being in a string if * we find a quote which is not prefixed by a \, for that * would be an escaped quote. However, a nasty situation can * occur where the string is like "test \\" as obvious, a * test for a \ in front of a " doesn't hold here for all * cases. Because "test \\\"" can exist as well, we need to * know if a quote is prefixed by an escaping slash or not. */ if (!inString) { inString = true; } else if (!escaped) { inString = false; } // reset escaped flag escaped = false; break; case '\t': if (!inString && (i > 0 && line.charAt(i - 1) == ',') || (i + 1 == len - 1 && line.charAt(++i) == ']')) { // dirty // split! if (line.charAt(cursor) == '"' && line.charAt(i - 2) == '"') { // reuse the StringBuilder by cleaning it helper.setLength(0); // prevent capacity increases helper.ensureCapacity((i - 2) - (cursor + 1)); for (int pos = cursor + 1; pos < i - 2; pos++) { if (line.charAt(pos) == '\\' && pos + 1 < i - 2) { pos++; // strToStr and strFromStr in gdk_atoms.mx only // support \t \n \\ \" and \377 switch (line.charAt(pos)) { case '\\': helper.append('\\'); break; case 'n': helper.append('\n'); break; case 't': helper.append('\t'); break; case '"': helper.append('"'); break; case '0': case '1': case '2': case '3': // this could be an octal number, let's check it out if (pos + 2 < i - 2 && line.charAt(pos + 1) >= '0' && line.charAt(pos + 1) <= '7' && line.charAt(pos + 2) >= '0' && line.charAt(pos + 2) <= '7') { // we got the number! try { helper.append((char)(Integer.parseInt("" + line.charAt(pos) + line.charAt(pos + 1) + line.charAt(pos + 2), 8))); pos += 2; } catch (NumberFormatException e) { // hmmm, this point should never be reached actually... throw new AssertionError("Flow error, should never try to parse non-number"); } } else { // do default action if number seems not to be correct helper.append(line.charAt(pos)); } break; default: // this is wrong, just ignore the escape, and print the char helper.append(line.charAt(pos)); break; } } else { helper.append(line.charAt(pos)); } } // put the unescaped string in the right place values[column] = OldMapiStringToJavaObjectConverter(helper.toString(), jDBCTypesMap[column]); } else if ((i - 1) - cursor == 4 && line.indexOf("NULL", cursor) == cursor) { values[column] = null; } else { values[column] = OldMapiStringToJavaObjectConverter(line.substring(cursor, i - 1), jDBCTypesMap[column]); } column++; cursor = i + 1; } // reset escaped flag escaped = false; break; } } // check if this result is of the size we expected it to be if (column != values.length) throw new ProtocolException("illegal result length: " + column + "\nlast read: " + (column > 0 ? values[column - 1] : "<none>")); return column; } private static final SimpleDateFormat DateParser = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat TimeParser = new SimpleDateFormat("HH:mm:ss"); private static final SimpleDateFormat TimestampParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static byte[] BinaryBlobConverter(String toParse) { int len = toParse.length() / 2; byte[] res = new byte[len]; for (int i = 0; i < len; i++) { res[i] = (byte) Integer.parseInt(toParse.substring(2 * i, (2 * i) + 2), 16); } return res; } private static Object OldMapiStringToJavaObjectConverter(String toParse, int jDBCMapping) throws ProtocolException { switch (jDBCMapping) { case Types.BIGINT: return Long.parseLong(toParse); case Types.BLOB: return new MonetBlob(BinaryBlobConverter(toParse)); case Types.BINARY: return BinaryBlobConverter(toParse); case Types.BOOLEAN: return Boolean.parseBoolean(toParse); case Types.CHAR: return toParse; case Types.CLOB: return new MonetClob(toParse); case Types.DATE: try { return DateParser.parse(toParse); } catch (ParseException e) { throw new ProtocolException(e.getMessage()); } case Types.DECIMAL: return new BigDecimal(toParse); case Types.DOUBLE: return Double.parseDouble(toParse); case Types.NUMERIC: return new BigInteger(toParse); case Types.INTEGER: return Integer.parseInt(toParse); case Types.REAL: return Float.parseFloat(toParse); case Types.SMALLINT: return Short.parseShort(toParse); case Types.TIME: try { return TimeParser.parse(toParse); } catch (ParseException e) { throw new ProtocolException(e.getMessage()); } case Types.TIMESTAMP: try { return TimestampParser.parse(toParse); } catch (ParseException e) { throw new ProtocolException(e.getMessage()); } case Types.TINYINT: return Byte.parseByte(toParse); case Types.VARCHAR: return toParse; default: return null; } } }