View Javadoc
1   /*
2    *    Copyright 2009-2024 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.reflection.wrapper;
17  
18  import static org.junit.jupiter.api.Assertions.*;
19  
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.LinkedHashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.ibatis.reflection.DefaultReflectorFactory;
27  import org.apache.ibatis.reflection.MetaObject;
28  import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
29  import org.apache.ibatis.reflection.factory.ObjectFactory;
30  import org.apache.ibatis.reflection.property.PropertyTokenizer;
31  import org.junit.jupiter.api.Test;
32  import org.junit.jupiter.params.ParameterizedTest;
33  import org.junit.jupiter.params.provider.CsvSource;
34  
35  class MapWrapperTest {
36  
37    @Test
38    void assertBasicOperations() {
39      Map<String, Object> map = new LinkedHashMap<>();
40      map.put("a", "100");
41      map.put("b", null);
42      map.put("my_name", Integer.valueOf(200));
43      MetaObject metaObj = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),
44          new DefaultReflectorFactory());
45      assertFalse(metaObj.isCollection());
46      assertTrue(metaObj.hasGetter("a"));
47      assertTrue(metaObj.hasSetter("a"));
48      assertTrue(metaObj.hasGetter("b.anykey"));
49      assertEquals("a", metaObj.findProperty("a", false));
50      assertEquals("b", metaObj.findProperty("b", false));
51      assertEquals("my_name", metaObj.findProperty("my_name", false));
52      assertEquals("my_name", metaObj.findProperty("my_name", true));
53      assertArrayEquals(new String[] { "a", "b", "my_name" }, metaObj.getGetterNames());
54      assertArrayEquals(new String[] { "a", "b", "my_name" }, metaObj.getSetterNames());
55      assertEquals(String.class, metaObj.getGetterType("a"));
56      assertEquals(Object.class, metaObj.getGetterType("b"));
57      assertEquals(Integer.class, metaObj.getGetterType("my_name"));
58      assertEquals(String.class, metaObj.getSetterType("a"));
59      assertEquals(Object.class, metaObj.getSetterType("b"));
60      assertEquals(Integer.class, metaObj.getSetterType("my_name"));
61      assertEquals("100", metaObj.getValue("a"));
62      assertNull(metaObj.getValue("b"));
63      assertEquals(Integer.valueOf(200), metaObj.getValue("my_name"));
64      try {
65        metaObj.add("x");
66        fail();
67      } catch (UnsupportedOperationException e) {
68        // pass
69      }
70      try {
71        metaObj.addAll(Arrays.asList("x", "y"));
72        fail();
73      } catch (UnsupportedOperationException e) {
74        // pass
75      }
76      metaObj.setValue("a", Long.valueOf(900L));
77      assertEquals(Long.valueOf(900L), map.get("a"));
78    }
79  
80    @Test
81    void assertNonExistentKey() {
82      Map<String, Object> map = new HashMap<>();
83      map.put("a", "100");
84      MetaObject metaObj = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),
85          new DefaultReflectorFactory());
86      assertEquals("anykey", metaObj.findProperty("anykey", false));
87      assertFalse(metaObj.hasGetter("anykey"));
88      assertFalse(metaObj.hasGetter("child.anykey"));
89      assertEquals(Object.class, metaObj.getGetterType("anykey"));
90      assertEquals(Object.class, metaObj.getGetterType("child.anykey"));
91      assertTrue(metaObj.hasSetter("anykey"));
92      assertTrue(metaObj.hasSetter("child.anykey"));
93      assertEquals(Object.class, metaObj.getSetterType("anykey"));
94      assertEquals(Object.class, metaObj.getSetterType("child.anykey"));
95      assertNull(metaObj.getValue("anykey"));
96  
97      metaObj.setValue("anykey", Integer.valueOf(200));
98      metaObj.setValue("child.anykey", Integer.valueOf(300));
99      assertEquals(3, map.size());
100     assertEquals(Integer.valueOf(200), map.get("anykey"));
101     @SuppressWarnings("unchecked")
102     Map<String, Object> childMap = (Map<String, Object>) map.get("child");
103     assertEquals(Integer.valueOf(300), childMap.get("anykey"));
104   }
105 
106   @Test
107   void assertChildBean() {
108     Map<String, Object> map = new HashMap<>();
109     TestBean bean = new TestBean();
110     bean.setPropA("aaa");
111     map.put("bean", bean);
112     MetaObject metaObj = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),
113         new DefaultReflectorFactory());
114     assertTrue(metaObj.hasGetter("bean.propA"));
115     assertEquals(String.class, metaObj.getGetterType("bean.propA"));
116     assertEquals(String.class, metaObj.getSetterType("bean.propA"));
117     assertEquals("aaa", metaObj.getValue("bean.propA"));
118 
119     assertNull(metaObj.getValue("bean.propB"));
120     metaObj.setValue("bean.propB", "bbb");
121     assertEquals("bbb", bean.getPropB());
122 
123     metaObj.setValue("bean.propA", "ccc");
124     assertEquals("ccc", bean.getPropA());
125   }
126 
127   static class TestBean {
128     private String propA;
129     private String propB;
130 
131     public String getPropA() {
132       return propA;
133     }
134 
135     public void setPropA(String propA) {
136       this.propA = propA;
137     }
138 
139     public String getPropB() {
140       return propB;
141     }
142 
143     public void setPropB(String propB) {
144       this.propB = propB;
145     }
146   }
147 
148   @Test
149   void accessIndexedList() {
150     Map<String, Object> map = new HashMap<>();
151     List<String> list = Arrays.asList("a", "b", "c");
152     map.put("list", list);
153     MetaObject metaObj = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),
154         new DefaultReflectorFactory());
155     assertEquals("b", metaObj.getValue("list[1]"));
156 
157     metaObj.setValue("list[2]", "x");
158     assertEquals("x", list.get(2));
159 
160     try {
161       metaObj.setValue("list[3]", "y");
162       fail();
163     } catch (IndexOutOfBoundsException e) {
164       // pass
165     }
166 
167     assertTrue(metaObj.hasGetter("list[1]"));
168     assertTrue(metaObj.hasSetter("list[1]"));
169     // this one looks wrong
170     // assertFalse(metaObj.hasSetter("list[3]"));
171   }
172 
173   @Test
174   void accessIndexedMap() {
175     Map<String, Object> map = new HashMap<>();
176     Map<String, String> submap = new HashMap<>();
177     submap.put("a", "100");
178     submap.put("b", "200");
179     submap.put("c", "300");
180     map.put("submap", submap);
181     MetaObject metaObj = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),
182         new DefaultReflectorFactory());
183     assertEquals("200", metaObj.getValue("submap[b]"));
184 
185     metaObj.setValue("submap[c]", "999");
186     assertEquals("999", submap.get("c"));
187 
188     metaObj.setValue("submap[d]", "400");
189     assertEquals(4, submap.size());
190     assertEquals("400", submap.get("d"));
191 
192     assertTrue(metaObj.hasGetter("submap[b]"));
193     assertTrue(metaObj.hasGetter("submap[anykey]"));
194     assertTrue(metaObj.hasSetter("submap[d]"));
195     assertTrue(metaObj.hasSetter("submap[anykey]"));
196   }
197 
198   @ParameterizedTest
199   @CsvSource({ "abc[def]", "abc.def", "abc.def.ghi", "abc[d.ef].ghi" })
200   void testCustomMapWrapper(String key) {
201     Map<String, Object> map = new HashMap<>();
202     MetaObject metaObj = MetaObject.forObject(map, new DefaultObjectFactory(), new FlatMapWrapperFactory(),
203         new DefaultReflectorFactory());
204     metaObj.setValue(key, "1");
205     assertEquals("1", map.get(key));
206     assertEquals("1", metaObj.getValue(key));
207   }
208 
209   static class FlatMapWrapperFactory implements ObjectWrapperFactory {
210     @Override
211     public boolean hasWrapperFor(Object object) {
212       return object instanceof Map;
213     }
214 
215     @SuppressWarnings("unchecked")
216     @Override
217     public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
218       return new FlatMapWrapper(metaObject, (Map<String, Object>) object, metaObject.getObjectFactory());
219     }
220   }
221 
222   static class FlatMapWrapper extends MapWrapper {
223     public FlatMapWrapper(MetaObject metaObject, Map<String, Object> map, ObjectFactory objectFactory) {
224       super(metaObject, map);
225     }
226 
227     @Override
228     public Object get(PropertyTokenizer prop) {
229       String key;
230       if (prop.getChildren() == null) {
231         key = prop.getIndexedName();
232       } else {
233         key = prop.getIndexedName() + "." + prop.getChildren();
234       }
235       return map.get(key);
236     }
237 
238     @Override
239     public void set(PropertyTokenizer prop, Object value) {
240       String key;
241       if (prop.getChildren() == null) {
242         key = prop.getIndexedName();
243       } else {
244         key = prop.getIndexedName() + "." + prop.getChildren();
245       }
246       map.put(key, value);
247     }
248   }
249 
250 }