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 }