BeanWrapperTest.java

/*
 *    Copyright 2009-2025 the original author or authors.
 *
 *    Licensed 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.ibatis.reflection.wrapper;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectionException;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.junit.jupiter.api.Test;

class BeanWrapperTest {

  @Test
  void assertBasicOperations() {
    Bean1 bean = new Bean1();
    MetaObject metaObj = MetaObject.forObject(bean, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),
        new DefaultReflectorFactory());
    assertFalse(metaObj.isCollection());
    assertTrue(metaObj.hasGetter("id"));
    assertTrue(metaObj.hasSetter("id"));
    assertTrue(metaObj.hasGetter("bean2.id"));
    assertTrue(metaObj.hasSetter("bean2.id"));
    assertEquals("id", metaObj.findProperty("id", false));
    assertNull(metaObj.findProperty("attr_val", false));
    assertEquals("attrVal", metaObj.findProperty("attr_val", true));
    String[] getterNames = metaObj.getGetterNames();
    Arrays.sort(getterNames);
    assertArrayEquals(new String[] { "attrVal", "bean2", "bean2List", "id", "nums" }, getterNames);
    String[] setterNames = metaObj.getSetterNames();
    Arrays.sort(setterNames);
    assertArrayEquals(new String[] { "attrVal", "bean2", "bean2List", "id", "nums" }, setterNames);
    assertEquals(String.class, metaObj.getGetterType("attrVal"));
    assertEquals(String.class, metaObj.getSetterType("attrVal"));
    assertEquals(String.class, metaObj.getGetterType("bean2.name"));
    assertEquals(String.class, metaObj.getSetterType("bean2.name"));

    assertTrue(metaObj.hasGetter("bean2List[0]"));
    try {
      metaObj.getValue("bean2List[0]");
      fail();
    } catch (ReflectionException e) {
      assertEquals("Cannot get the value 'bean2List[0]' because the property 'bean2List' is null.", e.getMessage());
    }
    try {
      metaObj.setValue("bean2List[0]", new Bean2());
      fail();
    } catch (ReflectionException e) {
      assertEquals("Cannot set the value 'bean2List[0]' because the property 'bean2List' is null.", e.getMessage());
    }
    assertTrue(metaObj.hasSetter("bean2List[0]"));

    List<Bean2> bean2List = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
      Bean2 bean2 = new Bean2();
      bean2.setId(i);
      bean2.setName("name_" + i);
      bean2List.add(bean2);
    }
    bean.setBean2List(bean2List);

    assertEquals(0, metaObj.getValue("bean2List[0].id"));

    metaObj.setValue("attrVal", "value");
    assertEquals("value", bean.getAttrVal());
    try {
      metaObj.getValue("attrVal[0]");
      fail();
    } catch (ReflectionException e) {
      assertEquals("Cannot get the value 'attrVal[0]' because the property 'attrVal' is not Map, List or Array.",
          e.getMessage());
    }
    try {
      metaObj.setValue("attrVal[0]", "blur");
      fail();
    } catch (ReflectionException e) {
      assertEquals("Cannot set the value 'attrVal[0]' because the property 'attrVal' is not Map, List or Array.",
          e.getMessage());
    }

    metaObj.setValue("bean2List[1].name", "new name 1");
    assertEquals("new name 1", bean.getBean2List().get(1).getName());

    try {
      metaObj.getValue("nums[0]");
      fail();
    } catch (ReflectionException e) {
      // pass
    }
    metaObj.setValue("nums", new Integer[] { 5, 6, 7 });
    assertTrue(metaObj.hasGetter("bean2List[0].child.id"));
    assertTrue(metaObj.hasSetter("bean2List[0].child.id"));

    {
      Bean2 bean2 = new Bean2();
      bean2.setId(100);
      bean2.setName("name_100");
      metaObj.setValue("bean2", bean2);
      assertEquals(String.class, metaObj.getGetterType("bean2.name"));
      assertEquals(String.class, metaObj.getSetterType("bean2.name"));
    }

    try {
      metaObj.setValue("bean2.child.bean2", "bogus");
      fail();
    } catch (ReflectionException e) {
      // pass
    }

    {
      Bean2 bean2 = new Bean2();
      bean2.setId(101);
      bean2.setName("name_101");
      metaObj.setValue("bean2.child.bean2", bean2);
      assertEquals(101, bean.getBean2().getChild().getBean2().getId());
    }

    metaObj.setValue("bean2.child.nums", new Integer[] { 8, 9 });
    metaObj.setValue("bean2.child.nums[0]", 88);
    assertEquals(88, bean.getBean2().getChild().getNums()[0]);

    assertFalse(metaObj.hasSetter("x[0].y"));
    assertFalse(metaObj.hasGetter("x[0].y"));

    try {
      metaObj.getValue("x");
      fail();
    } catch (ReflectionException e) {
      // pass
    }
    // assertEquals(Integer.class, metaObj.getSetterType("my_name"));
    // assertEquals("100", metaObj.getValue("a"));
    // assertNull(metaObj.getValue("b"));
    // assertEquals(Integer.valueOf(200), metaObj.getValue("my_name"));
    try {
      metaObj.add("x");
      fail();
    } catch (UnsupportedOperationException e) {
      // pass
    }
    try {
      metaObj.addAll(Arrays.asList("x", "y"));
      fail();
    } catch (UnsupportedOperationException e) {
      // pass
    }
  }

  static class Bean1 {
    private Integer id;
    private String attrVal;
    private Integer[] nums;
    private Bean2 bean2;
    private List<Bean2> bean2List;

    public Integer getId() {
      return id;
    }

    public void setId(Integer id) {
      this.id = id;
    }

    public String getAttrVal() {
      return attrVal;
    }

    public void setAttrVal(String attrVal) {
      this.attrVal = attrVal;
    }

    public Integer[] getNums() {
      return nums;
    }

    public void setNums(Integer[] nums) {
      this.nums = nums;
    }

    public Bean2 getBean2() {
      return bean2;
    }

    public void setBean2(Bean2 bean2) {
      this.bean2 = bean2;
    }

    public List<Bean2> getBean2List() {
      return bean2List;
    }

    public void setBean2List(List<Bean2> bean2List) {
      this.bean2List = bean2List;
    }
  }

  static class Bean2 {
    private Integer id;
    private String name;
    private Bean1 child;

    public Integer getId() {
      return id;
    }

    public void setId(Integer id) {
      this.id = id;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public Bean1 getChild() {
      return child;
    }

    public void setChild(Bean1 child) {
      this.child = child;
    }
  }
}