1 /*
2  * Copyright 2011 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.modelmapper.internal;
17
18 import java.lang.annotation.Annotation;
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Member;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Type;
23 import net.jodah.typetools.TypeResolver;
24 import org.modelmapper.spi.PropertyInfo;
25 import org.modelmapper.spi.PropertyType;
26 import org.modelmapper.spi.ValueReader;
27 import org.modelmapper.spi.ValueWriter;
28
29 /**
30  * Abstract PropertyInfo implementation that provides {@link #equals(Object)} and
31  * {@link #hashCode()} operations based on the property name.
32  * 
33  * @author Jonathan Halterman
34  */

35 abstract class PropertyInfoImpl<M extends Member> implements PropertyInfo {
36   protected final Class<?> initialType;
37   protected final M member;
38   protected final Class<?> type;
39   protected final String name;
40   private final PropertyType propertyType;
41
42   static abstract class AbstractMethodInfo extends PropertyInfoImpl<Method> {
43     private AbstractMethodInfo(Class<?> initialType, Method method, String name) {
44       super(initialType, method, PropertyType.METHOD, name);
45       method.setAccessible(true);
46     }
47
48     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
49       return member.getAnnotation(annotationClass);
50     }
51   }
52
53   static class FieldPropertyInfo extends PropertyInfoImpl<Field> implements Accessor, Mutator {
54     FieldPropertyInfo(Class<?> initialType, Field field, String name) {
55       super(initialType, field, PropertyType.FIELD, name);
56       field.setAccessible(true);
57     }
58
59     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
60       return member.getAnnotation(annotationClass);
61     }
62
63     public Type getGenericType() {
64       return member.getGenericType();
65     }
66
67     public Object getValue(Object subject) {
68       try {
69         return member.get(subject);
70       } catch (Exception e) {
71         throw new Errors().errorGettingValue(member, e).toMappingException();
72       }
73     }
74
75     public void setValue(Object subject, Object value) {
76       try {
77         member.set(subject, value);
78       } catch (Exception e) {
79         throw new Errors().errorSettingValue(member, value, e).toMappingException();
80       }
81     }
82
83     public TypeInfo<?> getTypeInfo(InheritingConfiguration configuration) {
84       return TypeInfoRegistry.typeInfoFor(this, configuration);
85     }
86   }
87
88   static class MethodAccessor extends AbstractMethodInfo implements Accessor {
89     MethodAccessor(Class<?> initialType, Method method, String name) {
90       super(initialType, method, name);
91     }
92
93     public Type getGenericType() {
94       return member.getGenericReturnType();
95     }
96
97     public Object getValue(Object subject) {
98       try {
99         return member.invoke(subject);
100       } catch (IllegalAccessException e) {
101         new Errors().errorAccessingProperty(this).throwMappingExceptionIfErrorsExist();
102         return null;
103       } catch (Exception e) {
104         throw new Errors().errorGettingValue(member, e).toMappingException();
105       }
106     }
107
108     public TypeInfo<?> getTypeInfo(InheritingConfiguration configuration) {
109       return TypeInfoRegistry.typeInfoFor(this, configuration);
110     }
111   }
112
113   static class MethodMutator extends AbstractMethodInfo implements Mutator {
114     MethodMutator(Class<?> initialType, Method method, String name) {
115       super(initialType, method, name);
116     }
117
118     public Type getGenericType() {
119       return member.getGenericParameterTypes()[0];
120     }
121
122     public void setValue(Object subject, Object value) {
123       try {
124         member.invoke(subject, value);
125       } catch (Exception e) {
126         throw new Errors().errorSettingValue(member, value, e).toMappingException();
127       }
128     }
129
130     public TypeInfo<?> getTypeInfo(InheritingConfiguration configuration) {
131       return TypeInfoRegistry.typeInfoFor(type, configuration);
132     }
133   }
134
135   static class ValueReaderPropertyInfo extends PropertyInfoImpl<Member> implements Accessor {
136     private ValueReader.Member<Object> valueReaderMember;
137
138     @SuppressWarnings("unchecked")
139     ValueReaderPropertyInfo(ValueReader.Member<?> valueReaderMember, Class<?> initialType, String name) {
140       super(initialType, null, PropertyType.GENERIC, name);
141       this.valueReaderMember = (ValueReader.Member<Object>) valueReaderMember;
142     }
143
144     @Override
145     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
146       return null;
147     }
148
149     @Override
150     public Type getGenericType() {
151       return type;
152     }
153
154     @Override
155     @SuppressWarnings("unchecked")
156     public Object getValue(Object subject) {
157       return valueReaderMember.get(subject, name);
158     }
159
160     @Override
161     public TypeInfo<?> getTypeInfo(InheritingConfiguration configuration) {
162       return TypeInfoRegistry.typeInfoFor(type, configuration);
163     }
164
165     static ValueReaderPropertyInfo fromMember(final ValueReader.Member<?> valueReaderMember, String memberName) {
166       if (valueReaderMember.getOrigin() != null) {
167         return new ValueReaderPropertyInfo(valueReaderMember, valueReaderMember.getValueType(), memberName) {
168           @Override
169           public TypeInfo<?> getTypeInfo(InheritingConfiguration configuration) {
170             return TypeInfoRegistry.typeInfoFor(valueReaderMember.getOrigin(),
171                 valueReaderMember.getValueType(), configuration);
172           }
173         };
174       }
175       return new ValueReaderPropertyInfo(valueReaderMember, valueReaderMember.getValueType(), memberName);
176     }
177
178     @SuppressWarnings("unchecked")
179     static ValueReaderPropertyInfo create(final ValueReader<?> valueReader, String memberName) {
180       final ValueReader<Object> uncheckedValueReader = (ValueReader<Object>) valueReader;
181       ValueReader.Member<?> valueReaderMember = new ValueReader.Member<Object>(Object.class) {
182         @Override
183         public Object get(Object source, String memberName) {
184           return uncheckedValueReader.get(source, memberName);
185         }
186       };
187       return new ValueReaderPropertyInfo(valueReaderMember, valueReaderMember.getValueType(), memberName);
188     }
189   }
190   static class ValueWriterPropertyInfo extends PropertyInfoImpl<Member> implements Mutator {
191     private ValueWriter.Member<Object> valueWriterMember;
192
193     @SuppressWarnings("unchecked")
194     ValueWriterPropertyInfo(ValueWriter.Member<?> valueWriterMember, Class<?> initialType, String name) {
195       super(initialType, null, PropertyType.GENERIC, name);
196       this.valueWriterMember = (ValueWriter.Member<Object>) valueWriterMember;
197     }
198
199     @Override
200     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
201       return null;
202     }
203
204     @Override
205     public Type getGenericType() {
206       return type;
207     }
208
209     @Override
210     public TypeInfo<?> getTypeInfo(InheritingConfiguration configuration) {
211       return TypeInfoRegistry.typeInfoFor(type, configuration);
212     }
213
214     @Override
215     public void setValue(Object subject, Object value) {
216       valueWriterMember.setValue(subject, value);
217     }
218
219     static ValueWriterPropertyInfo fromMember(final ValueWriter.Member<?> valueWriterMember, String memberName) {
220       if (valueWriterMember.getOrigin() != null) {
221         return new ValueWriterPropertyInfo(valueWriterMember, valueWriterMember.getValueType(), memberName) {
222           @Override
223           public TypeInfo<?> getTypeInfo(InheritingConfiguration configuration) {
224             return TypeInfoRegistry.typeInfoFor(valueWriterMember.getOrigin(),
225                 valueWriterMember.getValueType(), configuration);
226           }
227         };
228       }
229       return new ValueWriterPropertyInfo(valueWriterMember, valueWriterMember.getValueType(), memberName);
230     }
231
232     @SuppressWarnings("unchecked")
233     static ValueWriterPropertyInfo create(final ValueWriter<?> valueWriter, final String memberName) {
234       final ValueWriter<Object> uncheckedValueWriter = (ValueWriter<Object>) valueWriter;
235       ValueWriter.Member<?> valueWriterMember = new ValueWriter.Member<Object>(Object.class) {
236         @Override
237         public void setValue(Object source, Object value) {
238           uncheckedValueWriter.setValue(source, value, memberName);
239         }
240       };
241       return new ValueWriterPropertyInfo(valueWriterMember, valueWriterMember.getValueType(), memberName);
242     }
243   }
244
245   private PropertyInfoImpl(Class<?> initialType, M member, PropertyType propertyType, String name) {
246     this.initialType = initialType;
247     this.member = member;
248     this.propertyType = propertyType;
249     Type genericType = getGenericType();
250     this.type = genericType == null ? initialType : TypeResolver.resolveRawClass(genericType,
251         initialType);
252     this.name = name;
253   }
254
255   @Override
256   public boolean equals(Object o) {
257     if (this == o) {
258       return true;
259     }
260     if (o == null || getClass() != o.getClass()) {
261       return false;
262     }
263     PropertyInfoImpl<?> that = (PropertyInfoImpl<?>) o;
264     if (member != null ? !member.equals(that.member) : that.member != null) {
265       return false;
266     }
267     return name.equals(that.name);
268   }
269
270   public Class<?> getInitialType() {
271     return initialType;
272   }
273
274   public M getMember() {
275     return member;
276   }
277
278   public String getName() {
279     return name;
280   }
281
282   public PropertyType getPropertyType() {
283     return propertyType;
284   }
285
286   public Class<?> getType() {
287     return type;
288   }
289
290   @Override
291   public int hashCode() {
292     return (member == null ? 1 : member.getDeclaringClass().hashCode()) * 31 + name.hashCode();
293   }
294
295   @Override
296   public String toString() {
297     return member == null ? name : member.getDeclaringClass().getSimpleName() + "." + name;
298   }
299 }