View Javadoc
1   /*
2    *    Copyright 2009-2023 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;
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      }
77      return reflector.getSetterType(prop.getName());
78    }
79  
80    public Class<?> getGetterType(String name) {
81      PropertyTokenizer prop = new PropertyTokenizer(name);
82      if (prop.hasNext()) {
83        MetaClass metaProp = metaClassForProperty(prop);
84        return metaProp.getGetterType(prop.getChildren());
85      }
86      // issue #506. Resolve the type inside a Collection Object
87      return getGetterType(prop);
88    }
89  
90    private MetaClass metaClassForProperty(PropertyTokenizer prop) {
91      Class<?> propType = getGetterType(prop);
92      return MetaClass.forClass(propType, reflectorFactory);
93    }
94  
95    private Class<?> getGetterType(PropertyTokenizer prop) {
96      Class<?> type = reflector.getGetterType(prop.getName());
97      if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {
98        Type returnType = getGenericGetterType(prop.getName());
99        if (returnType instanceof ParameterizedType) {
100         Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
101         if (actualTypeArguments != null && actualTypeArguments.length == 1) {
102           returnType = actualTypeArguments[0];
103           if (returnType instanceof Class) {
104             type = (Class<?>) returnType;
105           } else if (returnType instanceof ParameterizedType) {
106             type = (Class<?>) ((ParameterizedType) returnType).getRawType();
107           }
108         }
109       }
110     }
111     return type;
112   }
113 
114   private Type getGenericGetterType(String propertyName) {
115     try {
116       Invoker invoker = reflector.getGetInvoker(propertyName);
117       if (invoker instanceof MethodInvoker) {
118         Field declaredMethod = MethodInvoker.class.getDeclaredField("method");
119         declaredMethod.setAccessible(true);
120         Method method = (Method) declaredMethod.get(invoker);
121         return TypeParameterResolver.resolveReturnType(method, reflector.getType());
122       }
123       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       return reflector.hasSetter(prop.getName());
139     }
140     if (reflector.hasSetter(prop.getName())) {
141       MetaClass metaProp = metaClassForProperty(prop.getName());
142       return metaProp.hasSetter(prop.getChildren());
143     }
144     return false;
145   }
146 
147   public boolean hasGetter(String name) {
148     PropertyTokenizer prop = new PropertyTokenizer(name);
149     if (!prop.hasNext()) {
150       return reflector.hasGetter(prop.getName());
151     }
152     if (reflector.hasGetter(prop.getName())) {
153       MetaClass metaProp = metaClassForProperty(prop);
154       return metaProp.hasGetter(prop.getChildren());
155     }
156     return false;
157   }
158 
159   public Invoker getGetInvoker(String name) {
160     return reflector.getGetInvoker(name);
161   }
162 
163   public Invoker getSetInvoker(String name) {
164     return reflector.getSetInvoker(name);
165   }
166 
167   private StringBuilder buildProperty(String name, StringBuilder builder) {
168     PropertyTokenizer prop = new PropertyTokenizer(name);
169     if (prop.hasNext()) {
170       String propertyName = reflector.findPropertyName(prop.getName());
171       if (propertyName != null) {
172         builder.append(propertyName);
173         builder.append(".");
174         MetaClass metaProp = metaClassForProperty(propertyName);
175         metaProp.buildProperty(prop.getChildren(), builder);
176       }
177     } else {
178       String propertyName = reflector.findPropertyName(name);
179       if (propertyName != null) {
180         builder.append(propertyName);
181       }
182     }
183     return builder;
184   }
185 
186   public boolean hasDefaultConstructor() {
187     return reflector.hasDefaultConstructor();
188   }
189 
190 }