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