content: package com.github.pgasync.conversion;
  
  import com.github.pgasync.Oid;
  
  import java.lang.reflect.Array;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.function.BiFunction;
  import java.util.function.Function;
  
  // TODO: change internal value format from byte[] to PgValue(TEXT|BINARY)
  @SuppressWarnings({"unchecked", "rawtypes"})
  class ArrayConversions {
  
      static String fromArray(final Object elements, final Function<Object, String> printFn) {
          return appendArray(new StringBuilder(), elements, printFn).toString();
      }
  
      private static StringBuilder appendArray(StringBuilder sb, final Object elements, final Function<Object, String> printFn) {
          sb.append('{');
  
          int nElements = Array.getLength(elements);
          for (int i = 0; i < nElements; i++) {
              if (i > 0) {
                  sb.append(',');
              }
  
              Object o = Array.get(elements, i);
              if (o == null) {
                  sb.append("NULL");
              } else if (o instanceof byte[]) {
                  sb.append(BlobConversions.fromBytes((byte[]) o));
              } else if (o.getClass().isArray()) {
                  sb = appendArray(sb, o, printFn);
              } else {
                  sb = appendEscaped(sb, printFn.apply(o));
              }
          }
  
          return sb.append('}');
      }
  
      private static StringBuilder appendEscaped(final StringBuilder b, final String s) {
          b.append('"');
          for (int j = 0; j < s.length(); j++) {
              char c = s.charAt(j);
              if (c == '"' || c == '\\') {
                  b.append('\\');
              }
  
              b.append(c);
          }
          return b.append('"');
      }
  
      static <T> T toArray(Class<T> arrayType, Oid oid, String value, BiFunction<Oid, String, Object> parse) {
          Class elementType = arrayType.getComponentType();
          while (elementType.getComponentType() != null && elementType != byte[].class) {
              elementType = elementType.getComponentType();
          }
          if (elementType.isPrimitive()) {
              throw new IllegalArgumentException("Primitive arrays are not supported due to possible NULL values");
          }
  
          if (value == null) {
              return null;
          }
  
          char[] text = value.toCharArray();
          List<List<Object>> holder = new ArrayList<>(1);
  
          if (readArray(text, skipBounds(text, 0), (List) holder) != text.length) {
              throw new IllegalStateException("Failed to read array");
          }
  
          return (T) toNestedArrays(holder.get(0), elementType, getElementOid(oid), parse);
      }
  
      private static int skipBounds(final char[] text, final int start) {
          if (text[start] != '[') {
              return start;
          }
          for (int end = start + 1; ; ) {
              if (text[end++] == '=') {
                  return end;
              }
          }
      }
  
      private static int readArray(final char[] text, final int start, List<Object> result) {
          List<Object> values = new ArrayList<>();
          for (int i = start + 1; ; ) {
              final char c = text[i];
              if (c == '}') {
                  result.add(values);
                  return i + 1;
              } else if (c == ',' || Character.isWhitespace(c)) {
                  i++;
              } else if (c == '"') {
                  i = readString(text, i, values);
              } else if (c == '{') {
                  i = readArray(text, i, values);
              } else if (c == 'N' && text.length > i + 4 &&
                      text[i + 1] == 'U' && text[i + 2] == 'L' && text[i + 3] == 'L' &&
                      (text[i + 4] == ',' || text[i + 4] == '}' || Character.isWhitespace(text[i + 4]))) {
                  i = readNull(i, values);
              } else {
                  i = readValue(text, i, values);
              }
          }
      }
  
      private static int readValue(final char[] text, final int start, List<Object> result) {
          StringBuilder str = new StringBuilder();
          for (int i = start; ; i++) {
              char c = text[i];
              if (c == ',' || c == '}' || Character.isWhitespace(c)) {
                  result.add(str.toString());
                  return i;
              }
              str.append(c);
          }
      }
  
      private static int readNull(final int i, final List<Object> result) {
          result.add(null);
          return i + 4;
      }
  
      private static int readString(final char[] text, final int start, final List<Object> result) {
          StringBuilder str = new StringBuilder();
          for (int i = start + 1; ; ) {
              char c = text[i++];
              if (c == '"') {
                  result.add(str.toString());
                  return i;
              }
              if (c == '\\') {
                  c = text[i++];
              }
              str.append(c);
          }
      }
  
      private static Oid getElementOid(final Oid oid) {
          try {
              return Oid.valueOf(oid.name().replaceFirst("_ARRAY", ""));
          } catch (IllegalArgumentException e) {
              return Oid.UNSPECIFIED;
          }
      }
  
      private static <T> T[] toNestedArrays(List<Object> result, Class<?> leafElementType, Oid oid, BiFunction<Oid, String, Object> parse) {
          Object[] arr = (Object[]) Array.newInstance(leafElementType, getNestedDimensions(result, oid));
          for (int i = 0; i < result.size(); i++) {
              Object elem = result.get(i);
              if (elem == null) {
                  arr[i] = null;
              } else if (elem.getClass().equals(String.class)) {
                  arr[i] = parse.apply(oid, (String) elem);
              } else {
                  arr[i] = toNestedArrays((List<Object>) elem, leafElementType, oid, parse);
              }
          }
          return (T[]) arr;
      }
  
      private static int[] getNestedDimensions(List<Object> result, Oid oid) {
          if (result.isEmpty()) {
              return new int[]{0};
          }
          if (!(result.get(0) instanceof List)) {
              return new int[]{result.size()};
          }
  
          List<Integer> dimensions = new ArrayList<>();
          dimensions.add(result.size());
  
          Object value = result.get(0);
          while (value instanceof List) {
              List nested = (List) value;
              dimensions.add(nested.size());
              value = nested.isEmpty() ? null : nested.get(0);
          }
  
          return toIntArray(dimensions);
      }
  
      private static int[] toIntArray(List<Integer> list) {
          int[] arr = new int[list.size()];
          for (int i = 0; i < arr.length; i++) {
              arr[i] = list.get(i);
          }
          return arr;
      }
  }
  