001 /*--------------------------------------------------------------------------+
002 $Id: ClassType.java 26283 2010-02-18 11:18:57Z juergens $
003 | |
004 | Copyright 2005-2010 Technische Universitaet Muenchen |
005 | |
006 | Licensed under the Apache License, Version 2.0 (the "License"); |
007 | you may not use this file except in compliance with the License. |
008 | You may obtain a copy of the License at |
009 | |
010 | http://www.apache.org/licenses/LICENSE-2.0 |
011 | |
012 | Unless required by applicable law or agreed to in writing, software |
013 | distributed under the License is distributed on an "AS IS" BASIS, |
014 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015 | See the License for the specific language governing permissions and |
016 | limitations under the License. |
017 +--------------------------------------------------------------------------*/
018 package edu.tum.cs.commons.reflect;
019
020 import java.util.HashSet;
021 import java.util.Set;
022
023 import edu.tum.cs.commons.collections.CollectionUtils;
024 import edu.tum.cs.commons.collections.UnmodifiableSet;
025
026 /**
027 * This stores the full type of a class, i.e., base type plus required
028 * interfaces. Primitive types are internally mapped to their corresponding
029 * class, so for example <code>int</code> and <code>Integer</code> are the
030 * same class type. This class is immutable.
031 * <p>
032 * The list of additional interfaces is kept normalized, such that no interface
033 * is in the list if it already implicitly given (either implemented by the base
034 * type or by another interface of the list).
035 *
036 * @author Benjamin Hummel
037 * @author $Author: juergens $
038 * @version $Rev: 26283 $
039 * @levd.rating GREEN Hash: 1BFC3AEF643BDF2D85CFF4282483AD06
040 */
041 public final class ClassType {
042
043 /** The base class of the class type. */
044 private Class<?> baseClass = Object.class;
045
046 /**
047 * All interfaces to be implemented by this type (normalized as described
048 * above).
049 */
050 private Set<Class<?>> interfaces = new HashSet<Class<?>>();
051
052 /**
053 * Creates the most general class type, that is of type Object without any
054 * additional interfaces.
055 */
056 public ClassType() {
057 // nothing to do
058 }
059
060 /** Copy constructor. */
061 private ClassType(ClassType c) {
062 baseClass = c.baseClass;
063 interfaces.addAll(c.interfaces);
064 }
065
066 /**
067 * Creates a new class type from a "normal" class.
068 */
069 public ClassType(Class<?> clazz) {
070 if (clazz.isInterface()) {
071 interfaces.add(clazz);
072 normalizeInterfaces();
073 } else {
074 baseClass = ReflectionUtils.resolvePrimitiveClass(clazz);
075 }
076 }
077
078 /**
079 * Creates a new class type from a list of "normal" class by either using it
080 * as the base class or adding it to the interface list.
081 *
082 * @throws TypesNotMergableException
083 * if the provided classes can not be joined.
084 */
085 public ClassType(Class<?>... classes) throws TypesNotMergableException {
086 for (Class<?> c : classes) {
087 mergeInClass(ReflectionUtils.resolvePrimitiveClass(c));
088 }
089 normalizeInterfaces();
090 }
091
092 /**
093 * Normalizes this class type by removing all interfaces implemented by the
094 * base class or already handled by other interfaces.
095 */
096 private void normalizeInterfaces() {
097 Set<Class<?>> oldInterfaces = interfaces;
098 interfaces = new HashSet<Class<?>>();
099
100 for (Class<?> iface : oldInterfaces) {
101 if (iface.isAssignableFrom(baseClass)) {
102 // base class can be assigned to interface (i.e. implements it),
103 // so there is no need to carry it around
104 continue;
105 }
106
107 boolean isCovered = false;
108 for (Class<?> otherIface : oldInterfaces) {
109 if ((iface != otherIface) && iface.isAssignableFrom(otherIface)) {
110 // more specific interface already in set, so iface is
111 // "covered" already
112 isCovered = true;
113 break;
114 }
115 }
116 if (!isCovered) {
117 interfaces.add(iface);
118 }
119 }
120 }
121
122 /**
123 * Merges the given class type with this type. This class and the merged in
124 * class is not modified in this process, and a newly created instance of
125 * the correct type is returned.
126 *
127 * @throws TypesNotMergableException
128 * if the new type could not be merged in.
129 */
130 public ClassType merge(ClassType classType)
131 throws TypesNotMergableException {
132 ClassType result = new ClassType(this);
133 result.interfaces.addAll(classType.interfaces);
134
135 result.mergeInClass(classType.baseClass);
136 result.normalizeInterfaces();
137
138 return result;
139 }
140
141 /**
142 * Merges the given class or interface with this type. The type is modified
143 * to also be compatible with the given class. This does not include
144 * normalization!
145 *
146 * @throws TypesNotMergableException
147 * if the new class could not be merged in.
148 */
149 private void mergeInClass(Class<?> c) throws TypesNotMergableException {
150 if (c.isInterface()) {
151 interfaces.add(c);
152 } else if (baseClass.equals(c)
153 || ReflectionUtils.isAssignable(baseClass, c)) {
154 // nothing to do, as base class is more specific than given class
155 } else if (ReflectionUtils.isAssignable(c, baseClass)) {
156 // c specializes the base class
157 baseClass = c;
158 } else {
159 throw new TypesNotMergableException("Types " + c + " and "
160 + baseClass + " could not be merged!");
161 }
162 }
163
164 /** Returns the base class for this ClassType. */
165 public Class<?> getBaseClass() {
166 return baseClass;
167 }
168
169 /**
170 * Returns whether this ClassType requires the implementation of interfaces
171 * in addition to the base class.
172 */
173 public boolean hasInterfaces() {
174 return !interfaces.isEmpty();
175 }
176
177 /**
178 * Returns all interfaces implemented by this class type in addition to the
179 * base class. The collection is normalized such that no interface is
180 * explicitly given if it is implemented by the base class or a super
181 * interface of another interface of the list.
182 */
183 public UnmodifiableSet<Class<?>> getInterfaces() {
184 return CollectionUtils.asUnmodifiable(interfaces);
185 }
186
187 /**
188 * Checks if an object of class type <code>classType</code> could be
189 * assigned to an object of this class type.
190 */
191 public boolean isAssignableFrom(ClassType classType) {
192 // we do not need the isAssignable from the ReflectionUtils here, as we
193 // store primitive types as they class right at construction time.
194 if (!baseClass.isAssignableFrom(classType.baseClass)) {
195 return false;
196 }
197
198 // all interfaces of "this" must also be fulfilled by classType
199 for (Class<?> iface : interfaces) {
200 if (!classType.implementsInterface(iface)) {
201 return false;
202 }
203 }
204
205 return true;
206 }
207
208 /** Returns whether the provided interface is implemented by this class type. */
209 public boolean implementsInterface(Class<?> requiredInterface) {
210 if (requiredInterface.isAssignableFrom(baseClass)) {
211 return true;
212 }
213 for (Class<?> iface : interfaces) {
214 if (requiredInterface.isAssignableFrom(iface)) {
215 return true;
216 }
217 }
218 return false;
219 }
220
221 /** {@inheritDoc} */
222 @Override
223 public String toString() {
224 // shortcut for plain interfaces
225 if (interfaces.size() == 1 && baseClass.equals(Object.class)) {
226 return interfaces.iterator().next().getName();
227 }
228
229 StringBuilder sb = new StringBuilder();
230 sb.append(baseClass.getName());
231 if (!interfaces.isEmpty()) {
232 String sep = " ";
233 sb.append(" implements");
234 for (Class<?> iface : interfaces) {
235 sb.append(sep);
236 sep = ", ";
237 sb.append(iface.getName());
238 }
239 }
240 return sb.toString();
241 }
242
243 /** {@inheritDoc} */
244 @Override
245 public boolean equals(Object obj) {
246 if (!(obj instanceof ClassType)) {
247 return false;
248 }
249 ClassType ct = (ClassType) obj;
250 if (!ct.baseClass.equals(baseClass)) {
251 return false;
252 }
253 if (ct.interfaces.size() != interfaces.size()) {
254 return false;
255 }
256 for (Class<?> iface : ct.interfaces) {
257 if (!interfaces.contains(iface)) {
258 return false;
259 }
260 }
261
262 return true;
263 }
264
265 /**
266 * Returns a hash code for this instance based on the base class and the
267 * additional (normalized) interfaces.
268 */
269 @Override
270 public int hashCode() {
271 int result = 1;
272 for (Class<?> iface : interfaces) {
273 result *= iface.hashCode();
274 }
275 return 13 * result * baseClass.hashCode();
276 }
277 }