1 package com.fasterxml.classmate;
2
3 import java.lang.reflect.TypeVariable;
4 import java.util.*;
5
6 /**
7  * Helper class used for storing binding of local type variables to
8  * matching resolved types, in context of a single class.
9  */

10 public final class TypeBindings
11 {
12     private final static String[] NO_STRINGS = new String[0];
13
14     private final static ResolvedType[] NO_TYPES = new ResolvedType[0];
15
16     private final static TypeBindings EMPTY = new TypeBindings(NO_STRINGS, NO_TYPES, null);
17
18     /**
19      * Array of type (type variable) names.
20      */

21     private final String[] _names;
22
23     /**
24      * Types matching names
25      */

26     private final ResolvedType[] _types;
27
28     /**
29      * Names of potentially unresolved type variables.
30      *
31      * @since 2.3
32      */

33     private final String[] _unboundVariables;
34     
35     private final int _hashCode;
36     
37     /*
38     /**********************************************************************
39     /* Construction
40     /**********************************************************************
41      */

42     
43     private TypeBindings(String[] names, ResolvedType[] types, String[] uvars)
44     {
45         _names = (names == null) ? NO_STRINGS : names;
46         _types = (types == null) ? NO_TYPES : types;
47         if (_names.length != _types.length) {
48             throw new IllegalArgumentException("Mismatching names ("+_names.length+"), types ("+_types.length+")");
49         }
50         int h = 1;
51         for (int i = 0, len = _types.length; i < len; ++i) {
52             h += _types[i].hashCode();
53         }
54         _unboundVariables = uvars;
55         _hashCode = h;
56     }
57
58     public static TypeBindings emptyBindings() {
59         return EMPTY;
60     }
61
62     /**
63      * Factory method for constructing bindings for given class using specified type
64      * parameters.
65      */

66     public static TypeBindings create(Class<?> erasedType, List<ResolvedType> typeList)
67     {
68         ResolvedType[] types = (typeList == null || typeList.isEmpty()) ?
69                 NO_TYPES : typeList.toArray(new ResolvedType[0]);
70         return create(erasedType, types);
71     }
72         
73     public static TypeBindings create(Class<?> erasedType, ResolvedType[] types)
74     {
75         if (types == null) {
76             types = NO_TYPES;
77         }
78         TypeVariable<?>[] vars = erasedType.getTypeParameters();
79         String[] names;
80         if (vars == null || vars.length == 0) {
81             names = NO_STRINGS;
82         } else {
83             int len = vars.length;
84             names = new String[len];
85             for (int i = 0; i < len; ++i) {
86                 names[i] = vars[i].getName();
87             }
88         }
89         // Check here to give better error message
90         if (names.length != types.length) {
91             throw new IllegalArgumentException("Can not create TypeBinding for class "+erasedType.getName()
92                    +" with "+types.length+" type parameter"
93                    +((types.length == 1) ? "" : "s")+": class expects "+names.length);
94         }
95         return new TypeBindings(names, types, null);
96     }
97
98     /**
99      * Method for creating an instance that has same bindings as this object,
100      * plus an indicator for additional type variable that may be unbound within
101      * this context; this is needed to resolve recursive self-references.
102      * 
103      * @since 1.3 (renamed from "withAdditionalBinding" in 1.2)
104      */

105     public TypeBindings withUnboundVariable(String name)
106     {
107         int len = (_unboundVariables == null) ? 0 : _unboundVariables.length;
108         String[] names =  (len == 0)
109                 ? new String[1] : Arrays.copyOf(_unboundVariables, len+1);
110         names[len] = name;
111         return new TypeBindings(_names, _types, names);
112     }
113
114     /*
115     /**********************************************************************
116     /* Accessors
117     /**********************************************************************
118      */

119     
120     /**
121      * Find type bound to specified name, if there is one; returns bound type if so, null if not.
122      */

123     public ResolvedType findBoundType(String name)
124     {
125         for (int i = 0, len = _names.length; i < len; ++i) {
126             if (name.equals(_names[i])) {
127                 return _types[i];
128             }
129         }
130         return null;
131     }
132
133     public boolean isEmpty() {
134         return (_types.length == 0);
135     }
136     
137     /**
138      * Returns number of bindings contained
139      */

140     public int size() { 
141         return _types.length;
142     }
143
144     public String getBoundName(int index)
145     {
146         if (index < 0 || index >= _names.length) {
147             return null;
148         }
149         return _names[index];
150     }
151
152     public ResolvedType getBoundType(int index)
153     {
154         if (index < 0 || index >= _types.length) {
155             return null;
156         }
157         return _types[index];
158     }
159
160     /**
161      * Accessor for getting bound types in declaration order
162      */

163     public List<ResolvedType> getTypeParameters()
164     {
165         if (_types.length == 0) {
166             return Collections.emptyList();
167         }
168         return Arrays.asList(_types);
169     }
170
171     /**
172      * @since 2.3
173      */

174     public boolean hasUnbound(String name) {
175         if (_unboundVariables != null) {
176             for (int i = _unboundVariables.length; --i >= 0; ) {
177                 if (name.equals(_unboundVariables[i])) {
178                     return true;
179                 }
180             }
181         }
182         return false;
183     }
184
185     /*
186     /**********************************************************************
187     /* Standard methods
188     /**********************************************************************
189      */

190     
191     @Override public String toString()
192     {
193         if (_types.length == 0) {
194             return "";
195         }
196         StringBuilder sb = new StringBuilder();
197         sb.append('<');
198         for (int i = 0, len = _types.length; i < len; ++i) {
199             if (i > 0) {
200                 sb.append(',');
201             }
202             sb = _types[i].appendBriefDescription(sb);
203         }
204         sb.append('>');
205         return sb.toString();
206     }
207
208     @Override public int hashCode() { return _hashCode; }
209
210     @Override public boolean equals(Object o)
211     {
212         if (o == thisreturn true;
213         if (o == null || o.getClass() != getClass()) return false;
214         TypeBindings other = (TypeBindings) o;
215         int len = _types.length;
216         if (len != other.size()) {
217             return false;
218         }
219         ResolvedType[] otherTypes = other._types;
220         for (int i = 0; i < len; ++i) {
221             if (!otherTypes[i].equals(_types[i])) {
222                 return false;
223             }
224         }
225         return true;
226     }
227     
228     /*
229     /**********************************************************************
230     /* Package accessible methods
231     /**********************************************************************
232      */

233
234     protected ResolvedType[] typeParameterArray() {
235         return _types;
236     }
237 }
238