package org.atomhopper.jdbc.query; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.Map; /** * This is class provides {@link java.sql.Array} interface for PostgreSQL * <code>text</code> array. * * @author Valentine Gogichashvili * */ public class PostgreSQLTextArray implements java.sql.Array { private final String[] stringArray; private final String stringValue; /** * Initializing constructor * * @param stringArray */ public PostgreSQLTextArray(String[] stringArray) { if (stringArray == null) { this.stringArray = null; } else { this.stringArray = Arrays.copyOf(stringArray, stringArray.length); } this.stringValue = stringArrayToPostgreSQLTextArray(this.stringArray); } @Override public String toString() { return stringValue; } private static final String NULL = "NULL"; /** * This static method can be used to convert an string array to string * representation of PostgreSQL text array. * * @param stringArray source String array * @return string representation of a given text array */ public static String stringArrayToPostgreSQLTextArray(String[] stringArray) { final int arrayLength; final int bufferAddition = 4; if (stringArray == null) { return NULL; } arrayLength = stringArray.length; if (arrayLength == 0) { return "{}"; } // count the string length and if need to quote int neededBufferLength = 2; // count the beginning '{' and the ending '}' brackets boolean[] shouldQuoteArray = new boolean[stringArray.length]; for (int si = 0; si < arrayLength; si++) { // count the comma after the first element if (si > 0) { neededBufferLength++; } boolean shouldQuote; final String s = stringArray[si]; if (s == null) { neededBufferLength += bufferAddition; shouldQuote = false; } else { final int l = s.length(); neededBufferLength += l; if (l == 0 || s.equalsIgnoreCase(NULL)) { shouldQuote = true; } else { shouldQuote = false; // scan for commas and quotes for (int i = 0; i < l; i++) { final char ch = s.charAt(i); switch (ch) { case '"': case '\\': shouldQuote = true; // we will escape these characters neededBufferLength++; break; case ',': case '\'': case '{': case '}': shouldQuote = true; break; default: if (Character.isWhitespace(ch)) { shouldQuote = true; } break; } } } // count the quotes if (shouldQuote) { neededBufferLength += 2; } } shouldQuoteArray[si] = shouldQuote; } // construct the String final StringBuilder sb = new StringBuilder(neededBufferLength); sb.append('{'); for (int si = 0; si < arrayLength; si++) { final String s = stringArray[si]; if (si > 0) { sb.append(','); } if (s == null) { sb.append(NULL); } else { final boolean shouldQuote = shouldQuoteArray[si]; if (shouldQuote) { sb.append('"'); } for (int i = 0, l = s.length(); i < l; i++) { final char ch = s.charAt(i); if (ch == '"' || ch == '\\') { sb.append('\\'); } sb.append(ch); } if (shouldQuote) { sb.append('"'); } } } sb.append('}'); assert sb.length() == neededBufferLength; return sb.toString(); } @Override public Object getArray() throws SQLException { return stringArray == null ? null : Arrays.copyOf(stringArray, stringArray.length); } @Override public Object getArray(Map<String, Class<?>> map) throws SQLException { return getArray(); } @Override public Object getArray(long index, int count) throws SQLException { return stringArray == null ? null : Arrays.copyOfRange(stringArray, (int) index, (int) index + count); } @Override public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException { return getArray(index, count); } @Override public int getBaseType() throws SQLException { return java.sql.Types.VARCHAR; } @Override public String getBaseTypeName() throws SQLException { return "text"; } @Override public ResultSet getResultSet() throws SQLException { throw new UnsupportedOperationException(); } @Override public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException { throw new UnsupportedOperationException(); } @Override public ResultSet getResultSet(long index, int count) throws SQLException { throw new UnsupportedOperationException(); } @Override public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) throws SQLException { throw new UnsupportedOperationException(); } @Override public void free() throws SQLException { } }