/*
 * Copyright (c) 2004, PostgreSQL Global Development Group
 * See the LICENSE file in the project root for more information.
 */

package org.postgresql.jdbc;

import org.postgresql.core.Oid;
import org.postgresql.core.TypeInfo;
import org.postgresql.util.ByteConverter;

import java.sql.Connection;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashMap;
import java.util.Map;

abstract class PrimitiveArraySupport<A> {

  public abstract int getDefaultArrayTypeOid(TypeInfo tiCache);

  public abstract String toArrayString(char delim, A array);

  public abstract void appendArray(StringBuilder sb, char delim, A array);

  public boolean supportBinaryRepresentation() {
    return true;
  }

  public abstract byte[] toBinaryRepresentation(Connection connection, A array) throws SQLFeatureNotSupportedException;

  private static final PrimitiveArraySupport<long[]> LONG_ARRAY = new PrimitiveArraySupport<long[]>() {

    
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.INT8_ARRAY; } @Override public String toArrayString(char delim, long[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void appendArray(StringBuilder sb, char delim, long[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i]); } sb.append('}'); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public byte[] toBinaryRepresentation(Connection connection, long[] array) { int length = 20 + (12 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.INT8); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 8; ByteConverter.int8(bytes, idx + 4, array[i]); idx += 12; } return bytes; } }; private static final PrimitiveArraySupport<int[]> INT_ARRAY = new PrimitiveArraySupport<int[]>() {
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.INT4_ARRAY; } @Override public String toArrayString(char delim, int[] array) { final StringBuilder sb = new StringBuilder(Math.max(32, array.length * 6)); appendArray(sb, delim, array); return sb.toString(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void appendArray(StringBuilder sb, char delim, int[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i]); } sb.append('}'); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public byte[] toBinaryRepresentation(Connection connection, int[] array) { int length = 20 + (8 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.INT4); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 4; ByteConverter.int4(bytes, idx + 4, array[i]); idx += 8; } return bytes; } }; private static final PrimitiveArraySupport<short[]> SHORT_ARRAY = new PrimitiveArraySupport<short[]>() {
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.INT2_ARRAY; } @Override public String toArrayString(char delim, short[] array) { final StringBuilder sb = new StringBuilder(Math.max(32, array.length * 4)); appendArray(sb, delim, array); return sb.toString(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void appendArray(StringBuilder sb, char delim, short[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i]); } sb.append('}'); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public byte[] toBinaryRepresentation(Connection connection, short[] array) { int length = 20 + (6 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.INT2); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 2; ByteConverter.int2(bytes, idx + 4, array[i]); idx += 6; } return bytes; } }; private static final PrimitiveArraySupport<double[]> DOUBLE_ARRAY = new PrimitiveArraySupport<double[]>() {
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.FLOAT8_ARRAY; } @Override public String toArrayString(char delim, double[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void appendArray(StringBuilder sb, char delim, double[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } // use quotes to account for any issues with scientific notation sb.append('"'); sb.append(array[i]); sb.append('"'); } sb.append('}'); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public byte[] toBinaryRepresentation(Connection connection, double[] array) { int length = 20 + (12 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.FLOAT8); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 8; ByteConverter.float8(bytes, idx + 4, array[i]); idx += 12; } return bytes; } }; private static final PrimitiveArraySupport<float[]> FLOAT_ARRAY = new PrimitiveArraySupport<float[]>() {
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.FLOAT4_ARRAY; } @Override public String toArrayString(char delim, float[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void appendArray(StringBuilder sb, char delim, float[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } // use quotes to account for any issues with scientific notation sb.append('"'); sb.append(array[i]); sb.append('"'); } sb.append('}'); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public byte[] toBinaryRepresentation(Connection connection, float[] array) { int length = 20 + (8 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.FLOAT4); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 4; ByteConverter.float4(bytes, idx + 4, array[i]); idx += 8; } return bytes; } }; private static final PrimitiveArraySupport<boolean[]> BOOLEAN_ARRAY = new PrimitiveArraySupport<boolean[]>() {
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.BOOL_ARRAY; } @Override public String toArrayString(char delim, boolean[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void appendArray(StringBuilder sb, char delim, boolean[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(array[i] ? '1' : '0'); } sb.append('}'); }
{@inheritDoc}
Throws:
/** * {@inheritDoc} * * @throws SQLFeatureNotSupportedException * Because this feature is not supported. */
@Override public byte[] toBinaryRepresentation(Connection connection, boolean[] array) throws SQLFeatureNotSupportedException { int length = 20 + (5 * array.length); final byte[] bytes = new byte[length]; // 1 dimension ByteConverter.int4(bytes, 0, 1); // no null ByteConverter.int4(bytes, 4, 0); // oid ByteConverter.int4(bytes, 8, Oid.BOOL); // length ByteConverter.int4(bytes, 12, array.length); int idx = 20; for (int i = 0; i < array.length; ++i) { bytes[idx + 3] = 1; ByteConverter.bool(bytes, idx + 4, array[i]); idx += 5; } return bytes; } }; private static final PrimitiveArraySupport<String[]> STRING_ARRAY = new PrimitiveArraySupport<String[]>() {
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getDefaultArrayTypeOid(TypeInfo tiCache) { return Oid.VARCHAR_ARRAY; } @Override public String toArrayString(char delim, String[] array) { final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8)); appendArray(sb, delim, array); return sb.toString(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void appendArray(StringBuilder sb, char delim, String[] array) { sb.append('{'); for (int i = 0; i < array.length; ++i) { if (i > 0) { sb.append(delim); } if (array[i] == null) { sb.append('N'); sb.append('U'); sb.append('L'); sb.append('L'); } else { PgArray.escapeArrayElement(sb, array[i]); } } sb.append('}'); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public boolean supportBinaryRepresentation() { return false; }
{@inheritDoc}
Throws:
/** * {@inheritDoc} * * @throws SQLFeatureNotSupportedException * Because this feature is not supported. */
@Override public byte[] toBinaryRepresentation(Connection connection, String[] array) throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(); } }; private static final Map<Class, PrimitiveArraySupport> ARRAY_CLASS_TO_SUPPORT = new HashMap<Class, PrimitiveArraySupport>((int) (7 / .75) + 1); static { ARRAY_CLASS_TO_SUPPORT.put(long[].class, LONG_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(int[].class, INT_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(short[].class, SHORT_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(double[].class, DOUBLE_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(float[].class, FLOAT_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(boolean[].class, BOOLEAN_ARRAY); ARRAY_CLASS_TO_SUPPORT.put(String[].class, STRING_ARRAY); } public static boolean isSupportedPrimitiveArray(Object obj) { return obj != null && ARRAY_CLASS_TO_SUPPORT.containsKey(obj.getClass()); } public static <A> PrimitiveArraySupport<A> getArraySupport(A array) { return ARRAY_CLASS_TO_SUPPORT.get(array.getClass()); } }