content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.arrow.vector.complex.impl;
  
  import static org.junit.jupiter.api.Assertions.assertThrows;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  
  import java.math.BigDecimal;
  import java.util.UUID;
  import org.apache.arrow.memory.BufferAllocator;
  import org.apache.arrow.memory.RootAllocator;
  import org.apache.arrow.vector.DecimalVector;
  import org.apache.arrow.vector.compare.VectorEqualsVisitor;
  import org.apache.arrow.vector.complex.FixedSizeListVector;
  import org.apache.arrow.vector.complex.ListVector;
  import org.apache.arrow.vector.complex.MapVector;
  import org.apache.arrow.vector.complex.StructVector;
  import org.apache.arrow.vector.complex.reader.FieldReader;
  import org.apache.arrow.vector.complex.writer.BaseWriter;
  import org.apache.arrow.vector.complex.writer.BaseWriter.ExtensionWriter;
  import org.apache.arrow.vector.complex.writer.BaseWriter.StructWriter;
  import org.apache.arrow.vector.complex.writer.FieldWriter;
  import org.apache.arrow.vector.holders.DecimalHolder;
  import org.apache.arrow.vector.types.Types;
  import org.apache.arrow.vector.types.pojo.ArrowType;
  import org.apache.arrow.vector.types.pojo.FieldType;
  import org.apache.arrow.vector.types.pojo.UuidType;
  import org.apache.arrow.vector.util.DecimalUtility;
  import org.junit.jupiter.api.AfterEach;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  
  public class TestComplexCopier {
  
    private BufferAllocator allocator;
  
    private static final int COUNT = 100;
  
    @BeforeEach
    public void init() {
      allocator = new RootAllocator(Long.MAX_VALUE);
    }
  
    @AfterEach
    public void terminate() throws Exception {
      allocator.close();
    }
  
    @Test
    public void testCopyFixedSizeListVector() {
      try (FixedSizeListVector from = FixedSizeListVector.empty("v", 3, allocator);
          FixedSizeListVector to = FixedSizeListVector.empty("v", 3, allocator)) {
  
        from.addOrGetVector(FieldType.nullable(Types.MinorType.INT.getType()));
        to.addOrGetVector(FieldType.nullable(Types.MinorType.INT.getType()));
  
        // populate from vector
        UnionFixedSizeListWriter writer = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          writer.startList();
          writer.integer().writeInt(i);
          writer.integer().writeInt(i * 2);
          writer.integer().writeInt(i * 3);
          writer.endList();
        }
        from.setValueCount(COUNT);
        to.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testInvalidCopyFixedSizeListVector() {
      try (FixedSizeListVector from = FixedSizeListVector.empty("v", 3, allocator);
          FixedSizeListVector to = FixedSizeListVector.empty("v", 2, allocator)) {
  
        from.addOrGetVector(FieldType.nullable(Types.MinorType.INT.getType()));
        to.addOrGetVector(FieldType.nullable(Types.MinorType.INT.getType()));
  
        // populate from vector
        UnionFixedSizeListWriter writer = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          writer.startList();
          writer.integer().writeInt(i);
          writer.integer().writeInt(i * 2);
          writer.integer().writeInt(i * 3);
          writer.endList();
        }
        from.setValueCount(COUNT);
        to.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        IllegalStateException e =
            assertThrows(IllegalStateException.class, () -> ComplexCopier.copy(in, out));
        assertTrue(e.getMessage().contains("greater than listSize"));
      }
    }
  
    @Test
    public void testCopyMapVector() {
      try (final MapVector from = MapVector.empty("v", allocator, false);
          final MapVector to = MapVector.empty("v", allocator, false)) {
  
        from.allocateNew();
  
        UnionMapWriter mapWriter = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          mapWriter.setPosition(i);
          mapWriter.startMap();
          mapWriter.startEntry();
          mapWriter.key().integer().writeInt(i);
          mapWriter.value().integer().writeInt(i);
          mapWriter.endEntry();
          mapWriter.startEntry();
          mapWriter.key().decimal().writeDecimal(BigDecimal.valueOf(i * 2));
          mapWriter.value().decimal().writeDecimal(BigDecimal.valueOf(i * 2));
          mapWriter.endEntry();
          mapWriter.endMap();
        }
  
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyListVector() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator)) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          listWriter.startList();
  
          listWriter.integer().writeInt(i);
          listWriter.integer().writeInt(i * 2);
  
          listWriter.list().startList();
          listWriter.list().bigInt().writeBigInt(i);
          listWriter.list().bigInt().writeBigInt(i * 2);
          listWriter.list().bigInt().writeBigInt(i * 3);
          listWriter.list().endList();
  
          listWriter.list().startList();
          listWriter.list().decimal().writeDecimal(BigDecimal.valueOf(i * 4));
          listWriter.list().decimal().writeDecimal(BigDecimal.valueOf(i * 5));
          listWriter.list().endList();
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyListVectorToANonEmptyList() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator)) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          listWriter.startList();
          listWriter.integer().writeInt(i);
          listWriter.integer().writeInt(i * 2);
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
        to.setValueCount(COUNT);
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
  
        // Copy again to the target vector which is non-empty
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
  
        // copy using copyFromSafe method
        for (int i = 0; i < COUNT; i++) {
          to.copyFromSafe(i, i, from);
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyListVectorWithNulls() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator)) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        // writer null, [null,i,null,i*2,null] alternatively
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          if (i % 2 == 0) {
            listWriter.writeNull();
            continue;
          }
          listWriter.startList();
          listWriter.integer().writeNull();
          listWriter.integer().writeInt(i);
          listWriter.integer().writeNull();
          listWriter.integer().writeInt(i * 2);
          listWriter.integer().writeNull();
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyListOfListVectorWithNulls() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator); ) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        // write null, [null,[50,100,null,200],null,
        // [null,50,null,100,null,200,null],null] alternatively
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          if (i % 2 == 0) {
            listWriter.writeNull();
            continue;
          }
          listWriter.startList();
          listWriter.list().writeNull();
          listWriter.list().startList();
          listWriter.list().bigInt().writeBigInt(50);
          listWriter.list().bigInt().writeBigInt(100);
          listWriter.list().bigInt().writeNull();
          listWriter.list().bigInt().writeBigInt(200);
          listWriter.list().endList();
          listWriter.list().writeNull();
          listWriter.list().startList();
          listWriter.list().bigInt().writeNull();
          listWriter.list().bigInt().writeBigInt(50);
          listWriter.list().bigInt().writeNull();
          listWriter.list().bigInt().writeBigInt(100);
          listWriter.list().bigInt().writeNull();
          listWriter.list().bigInt().writeBigInt(200);
          listWriter.list().bigInt().writeNull();
          listWriter.list().endList();
          listWriter.list().writeNull();
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyListOStructVectorWithNulls() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator); ) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        // write null, [null,{"f1":1,"f2":2},null,
        // {"f1":1,"f2":2},null] alternatively
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          if (i % 2 == 0) {
            listWriter.writeNull();
            continue;
          }
          listWriter.startList();
          listWriter.struct().writeNull();
          listWriter.struct().start();
          listWriter.struct().integer("f1").writeInt(1);
          listWriter.struct().integer("f2").writeInt(2);
          listWriter.struct().integer("f3").writeNull();
          listWriter.struct().end();
          listWriter.struct().writeNull();
          listWriter.struct().start();
          listWriter.struct().integer("f1").writeInt(1);
          listWriter.struct().integer("f2").writeInt(2);
          listWriter.struct().integer("f3").writeNull();
          listWriter.struct().end();
          listWriter.struct().writeNull();
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyListOfListOfStructVectorWithNulls() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator); ) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        // write null,
        // [null,[{"f1":50},null,{"f1":100},null,{"f1":200}],null,
        // [null,{"f1":50},null,{"f1":100},null,{"f1":200},null],null]
        // alternatively
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          if (i % 2 == 0) {
            listWriter.writeNull();
            continue;
          }
          listWriter.startList();
          listWriter.list().writeNull();
          listWriter.list().startList();
          listWriter.list().struct().start();
          listWriter.list().struct().bigInt("f1").writeBigInt(50);
          listWriter.list().struct().end();
          listWriter.list().struct().writeNull();
          listWriter.list().struct().start();
          listWriter.list().struct().bigInt("f1").writeBigInt(100);
          listWriter.list().struct().end();
          listWriter.list().struct().writeNull();
          listWriter.list().struct().start();
          listWriter.list().struct().bigInt("f1").writeBigInt(200);
          listWriter.list().struct().end();
          listWriter.list().endList();
  
          listWriter.list().writeNull();
  
          listWriter.list().startList();
          listWriter.list().struct().writeNull();
          listWriter.list().struct().start();
          listWriter.list().struct().bigInt("f1").writeBigInt(50);
          listWriter.list().struct().end();
  
          listWriter.list().struct().writeNull();
          listWriter.list().struct().start();
          listWriter.list().struct().bigInt("f1").writeBigInt(100);
          listWriter.list().struct().end();
  
          listWriter.list().struct().writeNull();
          listWriter.list().struct().start();
          listWriter.list().struct().bigInt("f1").writeBigInt(200);
          listWriter.list().struct().end();
  
          listWriter.list().struct().writeNull();
          listWriter.list().endList();
  
          listWriter.list().writeNull();
  
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testMapWithListValue() throws Exception {
      try (MapVector from = MapVector.empty("map", allocator, false);
          MapVector to = MapVector.empty("map", allocator, false)) {
  
        UnionMapWriter mapWriter = from.getWriter();
        BaseWriter.ListWriter valueWriter;
  
        /* allocate memory */
        mapWriter.allocate();
  
        // write null, [{},
        // {"value":[]},{"key":1,"value":[null,50,null,100,null,200,null]},
        // null,{"key":2,"value":[null,75,null,125,null,150,null,175,null]}]
        // alternatively
        for (int i = 0; i < COUNT; i++) {
          mapWriter.setPosition(i);
          if (i % 2 == 0) {
            mapWriter.writeNull();
            continue;
          }
  
          mapWriter.startMap();
  
          mapWriter.startEntry();
          mapWriter.key().bigInt().writeNull();
          mapWriter.value().list().writeNull();
          mapWriter.endEntry();
  
          mapWriter.startEntry();
          mapWriter.key().bigInt().writeNull();
          valueWriter = mapWriter.value().list();
          valueWriter.startList();
          valueWriter.endList();
          mapWriter.endEntry();
  
          mapWriter.startEntry();
          mapWriter.key().bigInt().writeBigInt(1);
          valueWriter = mapWriter.value().list();
          valueWriter.startList();
          valueWriter.bigInt().writeNull();
          valueWriter.bigInt().writeBigInt(50);
          valueWriter.bigInt().writeNull();
          valueWriter.bigInt().writeBigInt(100);
          valueWriter.bigInt().writeNull();
          valueWriter.bigInt().writeBigInt(200);
          valueWriter.bigInt().writeNull();
          valueWriter.endList();
          mapWriter.endEntry();
  
          mapWriter.writeNull();
  
          mapWriter.startEntry();
          mapWriter.key().bigInt().writeBigInt(2);
          valueWriter = mapWriter.value().list();
          valueWriter.startList();
          valueWriter.bigInt().writeNull();
          valueWriter.bigInt().writeBigInt(75);
          valueWriter.bigInt().writeNull();
          valueWriter.bigInt().writeBigInt(125);
          valueWriter.bigInt().writeNull();
          valueWriter.bigInt().writeBigInt(150);
          valueWriter.bigInt().writeNull();
          valueWriter.bigInt().writeBigInt(175);
          valueWriter.bigInt().writeNull();
          valueWriter.endList();
          mapWriter.endEntry();
  
          mapWriter.endMap();
        }
        mapWriter.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyFixedSizedListOfDecimalsVector() {
      try (FixedSizeListVector from = FixedSizeListVector.empty("v", 4, allocator);
          FixedSizeListVector to = FixedSizeListVector.empty("v", 4, allocator)) {
        from.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 0, 128)));
        to.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 0, 128)));
  
        DecimalHolder holder = new DecimalHolder();
        holder.buffer = allocator.buffer(DecimalVector.TYPE_WIDTH);
        ArrowType arrowType = new ArrowType.Decimal(3, 0, 128);
  
        // populate from vector
        UnionFixedSizeListWriter writer = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          writer.startList();
          writer.decimal().writeDecimal(BigDecimal.valueOf(i));
  
          DecimalUtility.writeBigDecimalToArrowBuf(
              new BigDecimal(i * 2), holder.buffer, 0, DecimalVector.TYPE_WIDTH);
          holder.start = 0;
          holder.scale = 0;
          holder.precision = 3;
          writer.decimal().write(holder);
  
          DecimalUtility.writeBigDecimalToArrowBuf(
              new BigDecimal(i * 3), holder.buffer, 0, DecimalVector.TYPE_WIDTH);
          writer.decimal().writeDecimal(0, holder.buffer, arrowType);
  
          writer
              .decimal()
              .writeBigEndianBytesToDecimal(
                  BigDecimal.valueOf(i * 4).unscaledValue().toByteArray(), arrowType);
  
          writer.endList();
        }
        from.setValueCount(COUNT);
        to.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
        holder.buffer.close();
      }
    }
  
    @Test
    public void testCopyUnionListWithDecimal() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator)) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          listWriter.startList();
  
          listWriter.decimal().writeDecimal(BigDecimal.valueOf(i * 2));
          listWriter.integer().writeInt(i);
          listWriter
              .decimal()
              .writeBigEndianBytesToDecimal(
                  BigDecimal.valueOf(i * 3).unscaledValue().toByteArray(),
                  new ArrowType.Decimal(3, 0, 128));
  
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyStructVector() {
      try (final StructVector from = StructVector.empty("v", allocator);
          final StructVector to = StructVector.empty("v", allocator)) {
  
        from.allocateNewSafe();
  
        NullableStructWriter structWriter = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          structWriter.setPosition(i);
          structWriter.start();
          structWriter.integer("int").writeInt(i);
          structWriter.decimal("dec", 0, 38).writeDecimal(BigDecimal.valueOf(i * 2));
          StructWriter innerStructWriter = structWriter.struct("struc");
          innerStructWriter.start();
          innerStructWriter.integer("innerint").writeInt(i * 3);
          innerStructWriter.decimal("innerdec", 0, 38).writeDecimal(BigDecimal.valueOf(i * 4));
          innerStructWriter
              .decimal("innerdec", 0, 38)
              .writeBigEndianBytesToDecimal(
                  BigDecimal.valueOf(i * 4).unscaledValue().toByteArray(),
                  new ArrowType.Decimal(3, 0, 128));
          innerStructWriter.end();
          structWriter.end();
        }
  
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyDecimalVectorWrongScale() {
      try (FixedSizeListVector from = FixedSizeListVector.empty("v", 3, allocator);
          FixedSizeListVector to = FixedSizeListVector.empty("v", 3, allocator)) {
        from.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 2, 128)));
        to.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 1, 128)));
  
        // populate from vector
        UnionFixedSizeListWriter writer = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          writer.startList();
          writer.decimal().writeDecimal(BigDecimal.valueOf(1.23));
          writer.decimal().writeDecimal(BigDecimal.valueOf(2.45));
          writer.endList();
        }
        from.setValueCount(COUNT);
        to.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        UnsupportedOperationException e =
            assertThrows(UnsupportedOperationException.class, () -> ComplexCopier.copy(in, out));
        assertTrue(
            e.getMessage().contains("BigDecimal scale must equal that in the Arrow vector: 2 != 1"));
      }
    }
  
    @Test
    public void testCopyStructVectorWithNulls() {
      try (StructVector from = StructVector.empty("v", allocator);
          StructVector to = StructVector.empty("v", allocator)) {
  
        NullableStructWriter writer = from.getWriter();
  
        for (int i = 0; i < COUNT; ++i) {
          writer.setPosition(i);
          writer.start();
          writer.integer("int").writeInt(i);
          if (i % 3 == 0) {
            writer.float4("child").writeFloat4(12.3f);
          } else if (i % 3 == 1) {
            writer.integer("child").writeInt(123);
          } else {
            writer.integer("child").writeNull();
          }
          writer.end();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyStructOfMap() {
      try (final StructVector from = StructVector.empty("v", allocator);
          final StructVector to = StructVector.empty("v", allocator); ) {
  
        from.allocateNew();
  
        NullableStructWriter structWriter = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          structWriter.setPosition(i);
          structWriter.start();
          BaseWriter.MapWriter innerMapWriter = structWriter.map("f1");
          innerMapWriter.startMap();
          innerMapWriter.startEntry();
          innerMapWriter.key().integer().writeInt(i);
          innerMapWriter.value().integer().writeInt(i);
          innerMapWriter.endEntry();
          innerMapWriter.endMap();
          structWriter.end();
        }
  
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyMapVectorWithMapValue() {
      try (final MapVector from = MapVector.empty("v", allocator, false);
          final MapVector to = MapVector.empty("v", allocator, false)) {
  
        from.allocateNew();
  
        UnionMapWriter mapWriter = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          mapWriter.setPosition(i);
          mapWriter.startMap();
          mapWriter.startEntry();
          mapWriter.key().integer().writeInt(i);
          BaseWriter.MapWriter innerMapWriter = mapWriter.value().map(false);
          innerMapWriter.startMap();
          innerMapWriter.startEntry();
          innerMapWriter.key().integer().writeInt(i);
          innerMapWriter.value().integer().writeInt(i);
          innerMapWriter.endEntry();
          innerMapWriter.endMap();
          mapWriter.endEntry();
          mapWriter.endMap();
        }
  
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out);
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyListVectorWithExtensionType() {
      try (ListVector from = ListVector.empty("v", allocator);
          ListVector to = ListVector.empty("v", allocator)) {
  
        UnionListWriter listWriter = from.getWriter();
        listWriter.allocate();
  
        for (int i = 0; i < COUNT; i++) {
          listWriter.setPosition(i);
          listWriter.startList();
          ExtensionWriter extensionWriter = listWriter.extension(new UuidType());
          extensionWriter.addExtensionTypeWriterFactory(new UuidWriterFactory());
          extensionWriter.writeExtension(UUID.randomUUID());
          extensionWriter.writeExtension(UUID.randomUUID());
          listWriter.endList();
        }
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out, new UuidWriterFactory());
        }
  
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyMapVectorWithExtensionType() {
      try (final MapVector from = MapVector.empty("v", allocator, false);
          final MapVector to = MapVector.empty("v", allocator, false)) {
  
        from.allocateNew();
  
        UnionMapWriter mapWriter = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          mapWriter.setPosition(i);
          mapWriter.startMap();
          mapWriter.startEntry();
          ExtensionWriter extensionKeyWriter = mapWriter.key().extension(new UuidType());
          extensionKeyWriter.addExtensionTypeWriterFactory(new UuidWriterFactory());
          extensionKeyWriter.writeExtension(UUID.randomUUID());
          ExtensionWriter extensionValueWriter = mapWriter.value().extension(new UuidType());
          extensionValueWriter.addExtensionTypeWriterFactory(new UuidWriterFactory());
          extensionValueWriter.writeExtension(UUID.randomUUID());
          mapWriter.endEntry();
          mapWriter.endMap();
        }
  
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out, new UuidWriterFactory());
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  
    @Test
    public void testCopyStructVectorWithExtensionType() {
      try (final StructVector from = StructVector.empty("v", allocator);
          final StructVector to = StructVector.empty("v", allocator)) {
  
        from.allocateNewSafe();
  
        NullableStructWriter structWriter = from.getWriter();
        for (int i = 0; i < COUNT; i++) {
          structWriter.setPosition(i);
          structWriter.start();
          ExtensionWriter extensionWriter1 = structWriter.extension("timestamp1", new UuidType());
          extensionWriter1.addExtensionTypeWriterFactory(new UuidWriterFactory());
          extensionWriter1.writeExtension(UUID.randomUUID());
          ExtensionWriter extensionWriter2 = structWriter.extension("timestamp2", new UuidType());
          extensionWriter2.addExtensionTypeWriterFactory(new UuidWriterFactory());
          extensionWriter2.writeExtension(UUID.randomUUID());
          structWriter.end();
        }
  
        from.setValueCount(COUNT);
  
        // copy values
        FieldReader in = from.getReader();
        FieldWriter out = to.getWriter();
        for (int i = 0; i < COUNT; i++) {
          in.setPosition(i);
          out.setPosition(i);
          ComplexCopier.copy(in, out, new UuidWriterFactory());
        }
        to.setValueCount(COUNT);
  
        // validate equals
        assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
      }
    }
  }
  