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.reflect.Field;
19 import java.lang.reflect.Method;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.concurrent.ConcurrentHashMap;
23
24 import org.modelmapper.config.Configuration;
25 import org.modelmapper.internal.PropertyInfoImpl.FieldPropertyInfo;
26 import org.modelmapper.internal.PropertyInfoImpl.MethodAccessor;
27 import org.modelmapper.internal.PropertyInfoImpl.MethodMutator;
28
29 /**
30  * Statically stores and retrieves MemberInfo by member and configuration. This registry is designed
31  * to return a distinct PropertyInfo instance for each initial type, member and configuration object
32  * set.
33  * 
34  * @author Jonathan Halterman
35  */

36 class PropertyInfoRegistry {
37
38   private static final Map<PropertyInfoKey, Mutator> MUTATOR_CACHE = new ConcurrentHashMap<PropertyInfoKey, Mutator>();
39   private static final Map<PropertyInfoKey, Accessor> ACCESSOR_CACHE = new ConcurrentHashMap<PropertyInfoKey, Accessor>();
40   private static final Map<PropertyInfoKey, FieldPropertyInfo> FIELD_CACHE = new ConcurrentHashMap<PropertyInfoKey, FieldPropertyInfo>();
41
42   private static class PropertyInfoKey {
43     private final Class<?> initialType;
44     private final String propertyName;
45     private final Configuration configuration;
46
47     PropertyInfoKey(Class<?> initialType, String propertyName, Configuration configuration) {
48       this.initialType = initialType;
49       this.propertyName = propertyName;
50       this.configuration = configuration;
51     }
52
53     @Override
54     public boolean equals(Object o) {
55       if (this == o)
56         return true;
57       if (!(o instanceof PropertyInfoKey))
58         return false;
59       PropertyInfoKey other = (PropertyInfoKey) o;
60       return initialType.equals(other.initialType) && propertyName.equals(other.propertyName)
61           && configuration.equals(other.configuration);
62     }
63
64     @Override
65     public int hashCode() {
66       int result = 31 + initialType.hashCode();
67       result = 31 * result + propertyName.hashCode();
68       result = 31 * result + configuration.hashCode();
69       return result;
70     }
71   }
72
73   /**
74    * Returns an accessor for the {@code accessorName}, else {@code nullif none exists.
75    */

76   static Accessor accessorFor(Class<?> type, String accessorName, InheritingConfiguration configuration) {
77     PropertyInfoKey key = new PropertyInfoKey(type, accessorName, configuration);
78     if (!ACCESSOR_CACHE.containsKey(key) && !FIELD_CACHE.containsKey(key)) {
79       @SuppressWarnings("unchecked")
80       Class<Object> uncheckedType = (Class<Object>) type;
81       for (Entry<String, Accessor> entry : TypeInfoRegistry.typeInfoFor(uncheckedType, configuration).getAccessors().entrySet()) {
82         if (entry.getValue().getMember() instanceof Method)
83           accessorFor(type, (Method) entry.getValue().getMember(), configuration, entry.getKey());
84         else if (entry.getValue().getMember() instanceof Field)
85           fieldPropertyFor(type, (Field) entry.getValue().getMember(), configuration, entry.getKey());
86       }
87     }
88
89     if (ACCESSOR_CACHE.containsKey(key))
90       return ACCESSOR_CACHE.get(key);
91     return FIELD_CACHE.get(key);
92   }
93
94   /**
95    * Returns an Accessor for the given accessor method. The method must be externally validated to
96    * ensure that it accepts zero arguments and does not return void.class.
97    */

98   static synchronized Accessor accessorFor(Class<?> type, Method method,
99       Configuration configuration, String name) {
100     PropertyInfoKey key = new PropertyInfoKey(type, name, configuration);
101     Accessor accessor = ACCESSOR_CACHE.get(key);
102     if (accessor == null) {
103       accessor = new MethodAccessor(type, method, name);
104       ACCESSOR_CACHE.put(key, accessor);
105     }
106
107     return accessor;
108   }
109
110   /**
111    * Returns a FieldPropertyInfo instance for the given field.
112    */

113   static synchronized FieldPropertyInfo fieldPropertyFor(Class<?> type, Field field,
114       Configuration configuration, String name) {
115     PropertyInfoKey key = new PropertyInfoKey(type, name, configuration);
116     FieldPropertyInfo fieldPropertyInfo = FIELD_CACHE.get(key);
117     if (fieldPropertyInfo == null) {
118       fieldPropertyInfo = new FieldPropertyInfo(type, field, name);
119       FIELD_CACHE.put(key, fieldPropertyInfo);
120     }
121
122     return fieldPropertyInfo;
123   }
124
125   /**
126    * Returns an mutator for the {@code accessorName}, else {@code nullif none exists.
127    * Returns a Mutator instance for the given mutator method. The method must be externally
128    * validated to ensure that it accepts one argument and returns void.class.
129    */

130   static synchronized Mutator mutatorFor(Class<?> type, String name, InheritingConfiguration configuration) {
131     PropertyInfoKey key = new PropertyInfoKey(type, name, configuration);
132     if (!MUTATOR_CACHE.containsKey(key) && !FIELD_CACHE.containsKey(key)) {
133       @SuppressWarnings("unchecked")
134       Class<Object> uncheckedType = (Class<Object>) type;
135       for (Entry<String, Mutator> entry : TypeInfoRegistry.typeInfoFor(uncheckedType, configuration).getMutators().entrySet()) {
136         if (entry.getValue().getMember() instanceof Method)
137           mutatorFor(type, (Method) entry.getValue().getMember(), configuration, entry.getKey());
138         else if (entry.getValue().getMember() instanceof Field)
139           fieldPropertyFor(type, (Field) entry.getValue().getMember(), configuration, entry.getKey());
140       }
141     }
142
143     if (MUTATOR_CACHE.containsKey(key))
144       return MUTATOR_CACHE.get(key);
145     return FIELD_CACHE.get(key);
146   }
147
148   /**
149    * Returns a Mutator instance for the given mutator method. The method must be externally
150    * validated to ensure that it accepts one argument and returns void.class.
151    */

152   static synchronized Mutator mutatorFor(Class<?> type, Method method, Configuration configuration,
153       String name) {
154     PropertyInfoKey key = new PropertyInfoKey(type, name, configuration);
155     Mutator mutator = MUTATOR_CACHE.get(key);
156     if (mutator == null) {
157       mutator = new MethodMutator(type, method, name);
158       MUTATOR_CACHE.put(key, mutator);
159     }
160
161     return mutator;
162   }
163 }
164