View Javadoc
1   /**
2    *    Copyright 2009-2018 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/PropertyTokenizer.html#PropertyTokenizer">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/PropertyTokenizer.html#PropertyTokenizer">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 _method = MethodInvoker.class.getDeclaredField("method");
120         _method.setAccessible(true);
121         Method method = (Method) _method.get(invoker);
122         return TypeParameterResolver.resolveReturnType(method, reflector.getType());
123       } else if (invoker instanceof GetFieldInvoker) {
124         Field _field = GetFieldInvoker.class.getDeclaredField("field");
125         _field.setAccessible(true);
126         Field field = (Field) _field.get(invoker);
127         return TypeParameterResolver.resolveFieldType(field, reflector.getType());
128       }
129     } catch (NoSuchFieldException | IllegalAccessException ignored) {
130     }
131     return null;
132   }
133 
134   public boolean hasSetter(String name) {
135     PropertyTokenizer/PropertyTokenizer.html#PropertyTokenizer">PropertyTokenizer prop = new PropertyTokenizer(name);
136     if (prop.hasNext()) {
137       if (reflector.hasSetter(prop.getName())) {
138         MetaClass metaProp = metaClassForProperty(prop.getName());
139         return metaProp.hasSetter(prop.getChildren());
140       } else {
141         return false;
142       }
143     } else {
144       return reflector.hasSetter(prop.getName());
145     }
146   }
147 
148   public boolean hasGetter(String name) {
149     PropertyTokenizer/PropertyTokenizer.html#PropertyTokenizer">PropertyTokenizer prop = new PropertyTokenizer(name);
150     if (prop.hasNext()) {
151       if (reflector.hasGetter(prop.getName())) {
152         MetaClass metaProp = metaClassForProperty(prop);
153         return metaProp.hasGetter(prop.getChildren());
154       } else {
155         return false;
156       }
157     } else {
158       return reflector.hasGetter(prop.getName());
159     }
160   }
161 
162   public Invoker getGetInvoker(String name) {
163     return reflector.getGetInvoker(name);
164   }
165 
166   public Invoker getSetInvoker(String name) {
167     return reflector.getSetInvoker(name);
168   }
169 
170   private StringBuilder buildProperty(String name, StringBuilder builder) {
171     PropertyTokenizer/PropertyTokenizer.html#PropertyTokenizer">PropertyTokenizer prop = new PropertyTokenizer(name);
172     if (prop.hasNext()) {
173       String propertyName = reflector.findPropertyName(prop.getName());
174       if (propertyName != null) {
175         builder.append(propertyName);
176         builder.append(".");
177         MetaClass metaProp = metaClassForProperty(propertyName);
178         metaProp.buildProperty(prop.getChildren(), builder);
179       }
180     } else {
181       String propertyName = reflector.findPropertyName(name);
182       if (propertyName != null) {
183         builder.append(propertyName);
184       }
185     }
186     return builder;
187   }
188 
189   public boolean hasDefaultConstructor() {
190     return reflector.hasDefaultConstructor();
191   }
192 
193 }