1 package com.fasterxml.classmate;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.Field;
5 import java.lang.reflect.Method;
6 import java.lang.reflect.Modifier;
7 import java.lang.reflect.Type;
8 import java.util.*;
9
10 import com.fasterxml.classmate.members.*;
11
12 public abstract class ResolvedType
13     implements Type
14 {
15     public final static ResolvedType[] NO_TYPES = new ResolvedType[0];
16
17     protected final static RawConstructor[] NO_CONSTRUCTORS = new RawConstructor[0];
18     protected final static RawField[] NO_FIELDS = new RawField[0];
19     protected final static RawMethod[] NO_METHODS = new RawMethod[0];
20     
21     protected final Class<?> _erasedType;
22
23     /**
24      * Type bindings active when resolving members (methods, fields,
25      * constructors) of this type
26      */

27     protected final TypeBindings _typeBindings;
28     
29     /*
30     /**********************************************************************
31     /* Life cycle
32     /**********************************************************************
33      */

34     
35     protected ResolvedType(Class<?> cls, TypeBindings bindings)
36     {
37         _erasedType = cls;
38         _typeBindings = (bindings == null) ? TypeBindings.emptyBindings() : bindings;
39     }
40     
41     /**
42      * Method that can be used to check if call to {@link TypeResolver#resolveSubtype(ResolvedType, Class)}
43      * may ever succeed; if false, it will fail with an exception, if true, it may succeed.
44      */

45     public abstract boolean canCreateSubtypes();
46
47     /**
48      * Method that can be used to check if call to {@link TypeResolver#resolveSubtype(ResolvedType, Class)}
49      * will succeed for specific type; if false, it will fail with an exception; if tru it
50      * will succeed.
51      */

52     public final boolean canCreateSubtype(Class<?> subtype) {
53         return canCreateSubtypes() && _erasedType.isAssignableFrom(subtype);
54     }
55     
56     /*
57     /**********************************************************************
58     /* Accessors for related types
59     /**********************************************************************
60      */

61     
62     /**
63      * Returns type-erased Class&lt;?&gt; that this resolved type has.
64      */

65     public Class<?> getErasedType() { return _erasedType; }
66
67     /**
68      * Returns parent class of this type, if it has one; primitive types
69      * and interfaces have no parent class, nor does Object type
70      * {@link java.lang.Object}.
71      * Also, placeholders for cyclic (recursive) types return null for
72      * this method.
73      */

74     public abstract ResolvedType getParentClass();
75
76     /**
77      * Accessor that must be used to find out actual type in
78      * case of "self-reference"case where type refers
79      * recursive to itself (like, <code>T implements Comparable&lt;T&gt;</code>).
80      * For all other types returns null but for self-references "real" type.
81      * Separate accessor is provided to avoid accidental infinite loops.
82      */

83     public abstract ResolvedType getSelfReferencedType();
84     
85     /**
86      * Method that can be used to access element type of array types; will return
87      * null for non-array types, and non-null type for array types.
88      */

89     public abstract ResolvedType getArrayElementType();
90
91     /**
92      * Returns ordered list of interfaces (in declaration order) that this type
93      * implements.
94      *
95      * @return List of interfaces this type implementsif any; empty list if none
96      */

97     public abstract List<ResolvedType> getImplementedInterfaces();
98
99     /**
100      * Returns list of generic type declarations for this type, in order they
101      * are declared in class description.
102      */

103     public List<ResolvedType> getTypeParameters() {
104         return _typeBindings.getTypeParameters();
105     }
106
107     /**
108      * Method for accessing bindings of type variables to resolved types in context
109      * of this type. It has same number of entries as return List of
110      * {@link #getTypeParameters}, accessible using declared name to which they
111      * bind; for example, {@link java.util.Map} has 2 type bindings; one for
112      * key type (name "K", from Map.java) and one for value type
113      * (name "V", from Map.java).
114      */

115     public TypeBindings getTypeBindings() { return _typeBindings; }
116     
117     /**
118      * Method that will try to find type parameterization this type
119      * has for specified super type
120      * 
121      * @return List of type parameters for specified supertype (which may
122      *   be empty, if supertype is not a parametric type); null if specified
123      *   type is not a super type of this type
124      */

125     public List<ResolvedType> typeParametersFor(Class<?> erasedSupertype)
126     {
127         ResolvedType type = findSupertype(erasedSupertype);
128         if (type != null) {
129             return type.getTypeParameters();
130         }
131         // nope; doesn't look like we extend or implement super type in question
132         return null;
133     }
134
135     /**
136      * Method for finding super type of this type that has specified type
137      * erased signature. If supertype is an interface which is implemented
138      * using multiple inheritance paths, preference is given to interfaces
139      * implemented "highest up the stack" (directly implemented interfaces
140      * over interfaces superclass implements).
141      */

142     public ResolvedType findSupertype(Class<?> erasedSupertype)
143     {
144         if (erasedSupertype == _erasedType) {
145             return this;
146         }
147         // Check super interfaces first:
148         if (erasedSupertype.isInterface()) {
149             for (ResolvedType it : getImplementedInterfaces()) {
150                 ResolvedType type = it.findSupertype(erasedSupertype);
151                 if (type != null) {
152                     return type;
153                 }
154             }
155         }
156         // and if not found, super class and its supertypes
157         ResolvedType pc = getParentClass();
158         if (pc != null) {
159             ResolvedType type = pc.findSupertype(erasedSupertype);
160             if (type != null) {
161                 return type;
162             }
163         }
164         // nope; doesn't look like we extend or implement super type in question
165         return null;
166     }
167     
168     /*
169     /**********************************************************************
170     /* Accessors for simple properties
171     /**********************************************************************
172      */

173     
174     public abstract boolean isInterface();
175     public final boolean isConcrete() { return !isAbstract(); }
176     public abstract boolean isAbstract();
177
178     /**
179      * Method that indicates whether this type is an array type.
180      */

181     public abstract boolean isArray();
182
183     /**
184      * Method that indicates whether this type is one of small number of primitive
185      * Java types; not including array types of primitive types but just basic
186      * primitive types.
187      */

188     public abstract boolean isPrimitive();
189
190     public final boolean isInstanceOf(Class<?> type) {
191         return type.isAssignableFrom(_erasedType);
192     }
193
194     /*
195     /**********************************************************************
196     /* Accessors for raw (minimally procesed) members
197     /**********************************************************************
198      */

199
200     public List<RawConstructor> getConstructors() { return Collections.emptyList(); }
201     public List<RawField> getMemberFields() { return Collections.emptyList(); }
202     public List<RawMethod> getMemberMethods() { return Collections.emptyList(); }
203     public List<RawField> getStaticFields() { return Collections.emptyList(); }
204     public List<RawMethod> getStaticMethods() { return Collections.emptyList(); }
205
206     /*
207     /**********************************************************************
208     /* String representations
209     /**********************************************************************
210      */

211
212     /**
213      * Method that returns full generic signature of the type; suitable
214      * as signature for things like ASM package.
215      */

216     public String getSignature() {
217         StringBuilder sb = new StringBuilder();
218         return appendSignature(sb).toString();
219     }
220
221     /**
222      * Method that returns type erased signature of the type; suitable
223      * as non-generic signature some packages need
224      */

225     public String getErasedSignature() {
226         StringBuilder sb = new StringBuilder();
227         return appendErasedSignature(sb).toString();
228     }
229
230     /**
231      * Human-readable full description of type, which includes specification
232      * of super types (in brief format)
233      */

234     public String getFullDescription() {
235         StringBuilder sb = new StringBuilder();
236         return appendFullDescription(sb).toString();
237     }
238
239     /**
240      * Human-readable brief description of type, which does not include
241      * information about super types.
242      */

243     public String getBriefDescription() {
244         StringBuilder sb = new StringBuilder();
245         return appendBriefDescription(sb).toString();
246     }
247
248     public abstract StringBuilder appendBriefDescription(StringBuilder sb);
249     public abstract StringBuilder appendFullDescription(StringBuilder sb);
250     public abstract StringBuilder appendSignature(StringBuilder sb);
251     public abstract StringBuilder appendErasedSignature(StringBuilder sb);
252     
253     /*
254     /**********************************************************************
255     /* Standard methods
256     /**********************************************************************
257      */

258
259     @Override public String toString() {
260         return getBriefDescription();
261     }
262
263     @Override public int hashCode() {
264         return _erasedType.getName().hashCode() + _typeBindings.hashCode();
265     }
266
267     @Override public boolean equals(Object o)
268     {
269         if (o == thisreturn true;
270         // sub-types must be same:
271         if (o == null || o.getClass() != getClass()) return false;
272         // Should be possible to actually implement here...
273         ResolvedType other = (ResolvedType) o;
274         if (other._erasedType != _erasedType) {
275             return false;
276         }
277         // and type bindings must match as well
278         return _typeBindings.equals(other._typeBindings);
279     }
280     
281     /*
282     /**********************************************************************
283     /* Helper methods for sub-classes; string construction
284     /**********************************************************************
285      */

286     
287     protected StringBuilder _appendClassSignature(StringBuilder sb)
288     {
289         sb.append('L');
290         sb = _appendClassName(sb);
291         int count = _typeBindings.size();
292         if (count > 0) {
293             sb.append('<');
294             for (int i = 0; i < count; ++i) {
295                 sb = _typeBindings.getBoundType(i).appendErasedSignature(sb);
296             }
297             sb.append('>');
298         }
299         sb.append(';');
300         return sb;
301     }
302
303     protected StringBuilder _appendErasedClassSignature(StringBuilder sb)
304     {
305         sb.append('L');
306         sb = _appendClassName(sb);
307         sb.append(';');
308         return sb;
309     }
310
311     protected StringBuilder _appendClassDescription(StringBuilder sb)
312     {
313         sb.append(_erasedType.getName());
314         int count = _typeBindings.size();
315         if (count > 0) {
316             sb.append('<');
317             for (int i = 0; i < count; ++i) {
318                 if (i > 0) {
319                     sb.append(',');
320                 }
321                 sb = _typeBindings.getBoundType(i).appendBriefDescription(sb);
322             }
323             sb.append('>');
324         }
325         return sb;
326     }
327     
328     protected StringBuilder _appendClassName(StringBuilder sb)
329     {
330         String name = _erasedType.getName();
331         for (int i = 0, len = name.length(); i < len; ++i) {
332             char c = name.charAt(i);
333             if (c == '.') c = '/';
334             sb.append(c);
335         }
336         return sb;
337     }
338
339     /*
340     /**********************************************************************
341     /* Helper methods for sub-classes; gathering members
342     /**********************************************************************
343      */

344     
345     /**
346      * @param statics Whether to return static methods (true) or member methods (false)
347      */

348     protected RawField[] _getFields(boolean statics)
349     {
350         ArrayList<RawField> fields = new ArrayList<RawField>();
351         for (Field f : _erasedType.getDeclaredFields()) {
352             // Only skip synthetic fields, which should not really be exposed
353             if (!f.isSynthetic()) {
354                 if (Modifier.isStatic(f.getModifiers()) == statics) {
355                     fields.add(new RawField(this, f));
356                 }
357             }
358         }
359         if (fields.isEmpty()) {
360             return NO_FIELDS;
361         }
362         return fields.toArray(new RawField[0]);
363     }
364
365     /**
366      * @param statics Whether to return static methods (true) or member methods (false)
367      */

368     protected RawMethod[] _getMethods(boolean statics)
369     {
370         ArrayList<RawMethod> methods = new ArrayList<RawMethod>();
371         for (Method m : _erasedType.getDeclaredMethods()) {
372             // Only skip synthetic fields, which should not really be exposed
373             if (!m.isSynthetic()) {
374                 if (Modifier.isStatic(m.getModifiers()) == statics) {
375                     methods.add(new RawMethod(this, m));
376                 }
377             }
378         }
379         if (methods.isEmpty()) {
380             return NO_METHODS;
381         }
382         return methods.toArray(new RawMethod[0]);
383     }
384
385     protected RawConstructor[] _getConstructors()
386     {
387         ArrayList<RawConstructor> ctors = new ArrayList<RawConstructor>();
388         for (Constructor<?> c : _erasedType.getDeclaredConstructors()) {
389             // Only skip synthetic fields, which should not really be exposed
390             if (!c.isSynthetic()) {
391                 ctors.add(new RawConstructor(this, c));
392             }
393         }
394         if (ctors.isEmpty()) {
395             return NO_CONSTRUCTORS;
396         }
397         return ctors.toArray(new RawConstructor[0]);
398     }
399 }
400