View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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;
17  
18  import java.lang.reflect.Field;
19  import java.lang.reflect.Method;
20  import java.lang.reflect.ParameterizedType;
21  import java.lang.reflect.Type;
22  import java.util.Collection;
23  
24  import org.apache.ibatis.reflection.invoker.GetFieldInvoker;
25  import org.apache.ibatis.reflection.invoker.Invoker;
26  import org.apache.ibatis.reflection.invoker.MethodInvoker;
27  import org.apache.ibatis.reflection.property.PropertyTokenizer;
28  
29  /**
30   * @author Clinton Begin
31   */
32  public class MetaClass {
33  
34    private final ReflectorFactory reflectorFactory;
35    private final Reflector reflector;
36  
37    private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
38      this.reflectorFactory = reflectorFactory;
39      this.reflector = reflectorFactory.findForClass(type);
40    }
41  
42    public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
43      return new MetaClass(type, reflectorFactory);
44    }
45  
46    public MetaClass metaClassForProperty(String name) {
47      Class<?> propType = reflector.getGetterType(name);
48      return MetaClass.forClass(propType, reflectorFactory);
49    }
50  
51    public String findProperty(String name) {
52      StringBuilder prop = buildProperty(name, new StringBuilder());
53      return prop.length() > 0 ? prop.toString() : null;
54    }
55  
56    public String findProperty(String name, boolean useCamelCaseMapping) {
57      if (useCamelCaseMapping) {
58        name = name.replace("_", "");
59      }
60      return findProperty(name);
61    }
62  
63    public String[] getGetterNames() {
64      return reflector.getGetablePropertyNames();
65    }
66  
67    public String[] getSetterNames() {
68      return reflector.getSetablePropertyNames();
69    }
70  
71    public Class<?> getSetterType(String name) {
72      PropertyTokenizer prop = new PropertyTokenizer(name);
73      if (prop.hasNext()) {
74        MetaClass metaProp = metaClassForProperty(prop.getName());
75        return metaProp.getSetterType(prop.getChildren());
76      } else {
77        return reflector.getSetterType(prop.getName());
78      }
79    }
80  
81    public Class<?> getGetterType(String name) {
82      PropertyTokenizer prop = new PropertyTokenizer(name);
83      if (prop.hasNext()) {
84        MetaClass metaProp = metaClassForProperty(prop);
85        return metaProp.getGetterType(prop.getChildren());
86      }
87      // issue #506. Resolve the type inside a Collection Object
88      return getGetterType(prop);
89    }
90  
91    private MetaClass metaClassForProperty(PropertyTokenizer prop) {
92      Class<?> propType = getGetterType(prop);
93      return MetaClass.forClass(propType, reflectorFactory);
94    }
95  
96    private Class<?> getGetterType(PropertyTokenizer prop) {
97      Class<?> type = reflector.getGetterType(prop.getName());
98      if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {
99        Type returnType = getGenericGetterType(prop.getName());
100       if (returnType instanceof ParameterizedType) {
101         Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
102         if (actualTypeArguments != null && actualTypeArguments.length == 1) {
103           returnType = actualTypeArguments[0];
104           if (returnType instanceof Class) {
105             type = (Class<?>) returnType;
106           } else if (returnType instanceof ParameterizedType) {
107             type = (Class<?>) ((ParameterizedType) returnType).getRawType();
108           }
109         }
110       }
111     }
112     return type;
113   }
114 
115   private Type getGenericGetterType(String propertyName) {
116     try {
117       Invoker invoker = reflector.getGetInvoker(propertyName);
118       if (invoker instanceof MethodInvoker) {
119         Field declaredMethod = MethodInvoker.class.getDeclaredField("method");
120         declaredMethod.setAccessible(true);
121         Method method = (Method) declaredMethod.get(invoker);
122         return TypeParameterResolver.resolveReturnType(method, reflector.getType());
123       } else if (invoker instanceof GetFieldInvoker) {
124         Field declaredField = GetFieldInvoker.class.getDeclaredField("field");
125         declaredField.setAccessible(true);
126         Field field = (Field) declaredField.get(invoker);
127         return TypeParameterResolver.resolveFieldType(field, reflector.getType());
128       }
129     } catch (NoSuchFieldException | IllegalAccessException e) {
130       // Ignored
131     }
132     return null;
133   }
134 
135   public boolean hasSetter(String name) {
136     PropertyTokenizer prop = new PropertyTokenizer(name);
137     if (prop.hasNext()) {
138       if (reflector.hasSetter(prop.getName())) {
139         MetaClass metaProp = metaClassForProperty(prop.getName());
140         return metaProp.hasSetter(prop.getChildren());
141       } else {
142         return false;
143       }
144     } else {
145       return reflector.hasSetter(prop.getName());
146     }
147   }
148 
149   public boolean hasGetter(String name) {
150     PropertyTokenizer prop = new PropertyTokenizer(name);
151     if (prop.hasNext()) {
152       if (reflector.hasGetter(prop.getName())) {
153         MetaClass metaProp = metaClassForProperty(prop);
154         return metaProp.hasGetter(prop.getChildren());
155       } else {
156         return false;
157       }
158     } else {
159       return reflector.hasGetter(prop.getName());
160     }
161   }
162 
163   public Invoker getGetInvoker(String name) {
164     return reflector.getGetInvoker(name);
165   }
166 
167   public Invoker getSetInvoker(String name) {
168     return reflector.getSetInvoker(name);
169   }
170 
171   private StringBuilder buildProperty(String name, StringBuilder builder) {
172     PropertyTokenizer prop = new PropertyTokenizer(name);
173     if (prop.hasNext()) {
174       String propertyName = reflector.findPropertyName(prop.getName());
175       if (propertyName != null) {
176         builder.append(propertyName);
177         builder.append(".");
178         MetaClass metaProp = metaClassForProperty(propertyName);
179         metaProp.buildProperty(prop.getChildren(), builder);
180       }
181     } else {
182       String propertyName = reflector.findPropertyName(name);
183       if (propertyName != null) {
184         builder.append(propertyName);
185       }
186     }
187     return builder;
188   }
189 
190   public boolean hasDefaultConstructor() {
191     return reflector.hasDefaultConstructor();
192   }
193 
194 }