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    }