Mercurial > hg > monetdb-java
view src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java @ 247:3897bbab2d23 embedded
Small fixes.
author | Pedro Ferreira <pedro.ferreira@monetdbsolutions.com> |
---|---|
date | Wed, 25 Jul 2018 17:56:48 +0200 (2018-07-25) |
parents | 5b13ccaba741 |
children | 4face9f42efc |
line wrap: on
line source
/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 1997 - July 2008 CWI, August 2008 - 2018 MonetDB B.V. */ package nl.cwi.monetdb.mcl.protocol.oldmapi; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.protocol.TableResultHeaders; import java.nio.CharBuffer; /** * The OldMapiTableHeaderParser is a generic parser that retrieves Q_TABLE, Q_PREPARE and Q_BLOCK responses data as * integers and Strings to fill the Tables' metadata. * * @author Fabian Groffen, Pedro Ferreira */ final class OldMapiTableHeaderParser { private OldMapiTableHeaderParser() {} /** * Retrieves the next table result set header and fills the respective array of values. * * @param lineBuffer An Old Mapi Protocol's lineBuffer to retrieve data * @param columnNames The result set column names * @param columnLengths The result set column lengths * @param types The result set column SQL types * @param tableNames The result set columns schema and table names in format schema.table * @return The integer representation of the Table Result Header retrieved * @throws ProtocolException If an error while parsing occurred */ static int getNextTableHeader(CharBuffer lineBuffer, String[] columnNames, int[] columnLengths, String[] types, String[] tableNames) throws ProtocolException { int res = TableResultHeaders.UNKNOWN; int currentLength = lineBuffer.limit(); char[] array = lineBuffer.array(); int pos = 0; boolean foundChar = false, nameFound = false; // find header name for (int i = currentLength - 1; i >= 0; i--) { switch (array[i]) { case ' ': case '\n': case '\t': case '\r': if (!foundChar) { currentLength = i - 1; } else { pos = i + 1; } break; case '#': // found! nameFound = true; if (pos == 0) pos = i + 1; i = 0; // force the loop to terminate break; default: foundChar = true; pos = 0; break; } } if (!nameFound) throw new ProtocolException("Invalid header, no header name found", pos); // depending on the name of the header, we continue switch (array[pos]) { case 'n': //name if (currentLength - pos == 4) { getStringValues(array, pos - 3, columnNames); res = TableResultHeaders.NAME; } break; case 'l': //length if (currentLength - pos == 6) { getIntValues(array, pos - 3, columnLengths); res = TableResultHeaders.LENGTH; } break; case 't': if (currentLength - pos == 4) { //type getStringValues(array, pos - 3, types); res = TableResultHeaders.TYPE; } else if (currentLength - pos == 10) { //table_name getStringValues(array, pos - 3, tableNames); res = TableResultHeaders.TABLE; } break; default: throw new ProtocolException("Unknown header: " + new String(array, pos, currentLength - pos)); } return res; } /** * Fills a String array header with values. * * As of Oct2014-SP1 release MAPI adds double quotes around names when the name contains a comma or a tab or a space * or a # or " character. * See issue: https://www.monetdb.org/bugzilla/show_bug.cgi?id=3616 If the parsed name string part has a " as first * and last character, we remove those added double quotes here. * * @param array The lineBuffer's backing array * @param stop The position to stop parsing * @param stringValues The String array to fill */ private static void getStringValues(char[] array, int stop, String[] stringValues) { int elem = 0, start = 2; boolean inString = false, escaped = false; for (int i = start; i < stop; i++) { switch(array[i]) { 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 ',': if (!inString && array[i + 1] == '\t') { // we found the field separator if (array[start] == '"') start++; // skip leading double quote if (elem < stringValues.length) { stringValues[elem++] = new String(array, start, i - (array[i - 1] == '"' ? 1 : 0) - start); } i++; start = i + 1; // reset start for the next name, skipping the field separator (a comma and tab) } // reset escaped flag escaped = false; break; default: escaped = false; break; } } // add the left over part (last column) if (array[start] == '"') start++; // skip leading double quote if (elem < stringValues.length) stringValues[elem] = new String(array, start, stop - (array[stop - 1] == '"' ? 1 : 0) - start); } /** * Fills an integer array header with values. * * @param array The lineBuffer's backing array * @param stop The position to stop parsing * @param intValues The integer array to fill * @throws ProtocolException If an error while parsing occurred */ private static void getIntValues(char[] array, int stop, int[] intValues) throws ProtocolException { int elem = 0, tmp = 0, start = 2; for (int i = start; i < stop; i++) { if (array[i] == ',' && array[i + 1] == '\t') { intValues[elem++] = tmp; tmp = 0; i++; } else { tmp *= 10; // note: don't use Character.isDigit() here, because we only want ISO-LATIN-1 digits if (array[i] >= '0' && array[i] <= '9') { tmp += (int) array[i] - (int)'0'; } else { throw new ProtocolException("Expected a digit in " + new String(array) + " at " + i); } } } // add the left over part intValues[elem] = tmp; } }