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
   *
   *     https://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.avro.specific;
  
  import static org.junit.jupiter.api.Assertions.*;
  
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import org.apache.avro.Schema;
  import org.apache.avro.Schema.Field;
  import org.apache.avro.Schema.Type;
  import org.apache.avro.generic.GenericData;
  import org.apache.avro.io.DatumWriter;
  import org.apache.avro.io.Encoder;
  import org.apache.avro.io.EncoderFactory;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  
  /*
   * If integerClass is primitive, reflection to find method will
   * result in a NoSuchMethodException in the case of a UNION schema
   */
  public class TestSpecificData {
  
    private Class<?> intClass;
    private Class<?> integerClass;
  
    @BeforeEach
    public void setUp() {
      Schema intSchema = Schema.create(Type.INT);
      intClass = SpecificData.get().getClass(intSchema);
      Schema nullSchema = Schema.create(Type.NULL);
      Schema nullIntUnionSchema = Schema.createUnion(Arrays.asList(nullSchema, intSchema));
      integerClass = SpecificData.get().getClass(nullIntUnionSchema);
    }
  
    @Test
    void classTypes() {
      assertTrue(intClass.isPrimitive());
      assertFalse(integerClass.isPrimitive());
    }
  
    @Test
    void primitiveParam() throws Exception {
      assertNotNull(Reflection.class.getMethod("primitive", intClass));
    }
  
    @Test
    void primitiveParamError() throws Exception {
      assertThrows(NoSuchMethodException.class, () -> {
        Reflection.class.getMethod("primitiveWrapper", intClass);
      });
    }
  
    @Test
    void primitiveWrapperParam() throws Exception {
      assertNotNull(Reflection.class.getMethod("primitiveWrapper", integerClass));
    }
  
    @Test
    void primitiveWrapperParamError() throws Exception {
      assertThrows(NoSuchMethodException.class, () -> {
        Reflection.class.getMethod("primitive", integerClass);
      });
    }
  
    static class Reflection {
      public void primitive(int i) {
      }
  
      public void primitiveWrapper(Integer i) {
      }
    }
  
    public static class TestRecord extends SpecificRecordBase {
      private static final Schema SCHEMA = Schema.createRecord("TestRecord", null, null, false);
      static {
        List<Field> fields = new ArrayList<>();
        fields.add(new Field("x", Schema.create(Type.INT), null, null));
        Schema stringSchema = Schema.create(Type.STRING);
        GenericData.setStringType(stringSchema, GenericData.StringType.String);
        fields.add(new Field("y", stringSchema, null, null));
        SCHEMA.setFields(fields);
      }
      private int x;
      private String y;
  
      @Override
      public void put(int i, Object v) {
        switch (i) {
        case 0:
          x = (Integer) v;
          break;
        case 1:
          y = (String) v;
          break;
        default:
          throw new RuntimeException();
        }
      }
  
      @Override
      public Object get(int i) {
        switch (i) {
        case 0:
          return x;
        case 1:
          return y;
        }
        throw new RuntimeException();
      }
  
      @Override
      public Schema getSchema() {
        return SCHEMA;
      }
  
    }
  
    @Test
    void specificRecordBase() {
      final TestRecord record = new TestRecord();
      record.put("x", 1);
      record.put("y", "str");
      assertEquals(1, record.get("x"));
      assertEquals("str", record.get("y"));
    }
  
    @Test
    void externalizeable() throws Exception {
      final TestRecord before = new TestRecord();
      before.put("x", 1);
      before.put("y", "str");
      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(bytes);
      out.writeObject(before);
      out.close();
  
      ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()));
      TestRecord after = (TestRecord) in.readObject();
  
      assertEquals(before, after);
    }
  
    /** Tests that non Stringable datum are rejected by specific writers. */
    @Test
    void nonStringable() throws Exception {
      final Schema string = Schema.create(Type.STRING);
      final ByteArrayOutputStream baos = new ByteArrayOutputStream();
      final Encoder encoder = EncoderFactory.get().directBinaryEncoder(baos, null);
      final DatumWriter<Object> writer = new SpecificDatumWriter<>(string);
      try {
        writer.write(new Object(), encoder);
        fail("Non stringable object should be rejected.");
      } catch (ClassCastException cce) {
        // Expected error
      }
    }
  
    @Test
    void testToByteArray() throws Exception {
      final Schema string = Schema.create(Type.STRING);
      final DatumWriter<String> writer = new SpecificDatumWriter<>(string);
  
      try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        final Encoder encoder = EncoderFactory.get().directBinaryEncoder(baos, null);
        writer.write("test", encoder);
  
        final byte[] bytes = writer.toByteArray("test");
        assertArrayEquals(baos.toByteArray(), bytes);
      }
    }
  
    @Test
    void classNameContainingReservedWords() {
      final Schema schema = Schema.createRecord("AnyName", null, "db.public.table", false);
  
      assertEquals("db.public$.table.AnyName", SpecificData.getClassName(schema));
    }
  
    @Test
    void testCanGetClassOfMangledType() {
      assertEquals("org.apache.avro.specific.int$", SpecificData.getClassName(int$.getClassSchema()));
    }
  }
  