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