blob: 567415bfab90889073bfb54d1d3a33e21b762435 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.doclava;
18
19import com.google.clearsilver.jsilver.data.Data;
20
21import java.util.*;
22
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070023public class TypeInfo implements Resolvable {
Ben Dodson920dbbb2010-08-04 15:21:06 -070024 public static final Set<String> PRIMITIVE_TYPES = Collections.unmodifiableSet(
25 new HashSet<String>(Arrays.asList("boolean", "byte", "char", "double", "float", "int",
26 "long", "short", "void")));
27
28 public TypeInfo(boolean isPrimitive, String dimension, String simpleTypeName,
29 String qualifiedTypeName, ClassInfo cl) {
30 mIsPrimitive = isPrimitive;
31 mDimension = dimension;
32 mSimpleTypeName = simpleTypeName;
33 mQualifiedTypeName = qualifiedTypeName;
34 mClass = cl;
35 }
36
37 public TypeInfo(String typeString) {
38 // VarArgs
39 if (typeString.endsWith("...")) {
40 typeString = typeString.substring(0, typeString.length() - 3);
41 }
42
43 // Generic parameters
44 int paramStartPos = typeString.indexOf('<');
45 if (paramStartPos > -1) {
46 ArrayList<TypeInfo> generics = new ArrayList<TypeInfo>();
47 int paramEndPos = typeString.lastIndexOf('>');
48
49 int entryStartPos = paramStartPos + 1;
50 int bracketNesting = 0;
51 for (int i = entryStartPos; i < paramEndPos; i++) {
52 char c = typeString.charAt(i);
53 if (c == ',' && bracketNesting == 0) {
54 String entry = typeString.substring(entryStartPos, i).trim();
55 TypeInfo info = new TypeInfo(entry);
56 generics.add(info);
57 entryStartPos = i + 1;
58 } else if (c == '<') {
59 bracketNesting++;
60 } else if (c == '>') {
61 bracketNesting--;
62 }
63 }
64
65 TypeInfo info = new TypeInfo(typeString.substring(entryStartPos, paramEndPos).trim());
66 generics.add(info);
67
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070068 mTypeArguments = generics;
Ben Dodson920dbbb2010-08-04 15:21:06 -070069
70 if (paramEndPos < typeString.length() - 1) {
71 typeString = typeString.substring(0,paramStartPos) + typeString.substring(paramEndPos + 1);
72 } else {
73 typeString = typeString.substring(0,paramStartPos);
74 }
75 }
76
77 // Dimensions
78 int pos = typeString.indexOf('[');
79 if (pos > -1) {
80 mDimension = typeString.substring(pos);
81 typeString = typeString.substring(0, pos);
82 } else {
83 mDimension = "";
84 }
85
86 if (PRIMITIVE_TYPES.contains(typeString)) {
87 mIsPrimitive = true;
88 mSimpleTypeName = typeString;
89 mQualifiedTypeName = typeString;
90 } else {
91 mQualifiedTypeName = typeString;
92 pos = typeString.lastIndexOf('.');
93 if (pos > -1) {
94 mSimpleTypeName = typeString.substring(pos + 1);
95 } else {
96 mSimpleTypeName = typeString;
97 }
98 }
99 }
100
Jeff Arnesond6570b02014-10-29 15:46:51 -0700101 /**
102 * Copy Constructor.
103 */
104 private TypeInfo(TypeInfo other) {
105 mIsPrimitive = other.isPrimitive();
106 mIsTypeVariable = other.isTypeVariable();
107 mIsWildcard = other.isWildcard();
108 mDimension = other.dimension();
109 mSimpleTypeName = other.simpleTypeName();
110 mQualifiedTypeName = other.qualifiedTypeName();
111 mClass = other.asClassInfo();
112 if (other.typeArguments() != null) {
113 mTypeArguments = new ArrayList<TypeInfo>(other.typeArguments());
114 }
115 if (other.superBounds() != null) {
116 mSuperBounds = new ArrayList<TypeInfo>(other.superBounds());
117 }
118 if (other.extendsBounds() != null) {
119 mExtendsBounds = new ArrayList<TypeInfo>(other.extendsBounds());
120 }
121 mFullName = other.fullName();
122 }
123
Ben Dodson920dbbb2010-08-04 15:21:06 -0700124 public ClassInfo asClassInfo() {
125 return mClass;
126 }
127
128 public boolean isPrimitive() {
129 return mIsPrimitive;
130 }
131
132 public String dimension() {
133 return mDimension;
134 }
135
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700136 public void setDimension(String dimension) {
137 mDimension = dimension;
138 }
139
Ben Dodson920dbbb2010-08-04 15:21:06 -0700140 public String simpleTypeName() {
141 return mSimpleTypeName;
142 }
143
144 public String qualifiedTypeName() {
145 return mQualifiedTypeName;
146 }
147
148 public String fullName() {
149 if (mFullName != null) {
150 return mFullName;
151 } else {
152 return fullName(new HashSet<String>());
153 }
154 }
155
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700156 public static String typeArgumentsName(ArrayList<TypeInfo> args, HashSet<String> typeVars) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700157 String result = "<";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700158
159 int i = 0;
160 for (TypeInfo arg : args) {
161 result += arg.fullName(typeVars);
162 if (i != (args.size()-1)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700163 result += ", ";
164 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700165 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700166 }
167 result += ">";
168 return result;
169 }
170
171 public String fullName(HashSet<String> typeVars) {
172 mFullName = fullNameNoDimension(typeVars) + mDimension;
173 return mFullName;
174 }
175
Florian Uunk434521c2014-04-07 17:25:11 +0100176 public String fullNameNoBounds(HashSet<String> typeVars) {
177 return fullNameNoDimensionNoBounds(typeVars) + mDimension;
178 }
179
180 // don't recurse forever with the parameters. This handles
181 // Enum<K extends Enum<K>>
182 private boolean checkRecurringTypeVar(HashSet<String> typeVars) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700183 if (mIsTypeVariable) {
184 if (typeVars.contains(mQualifiedTypeName)) {
Florian Uunk434521c2014-04-07 17:25:11 +0100185 return true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700186 }
187 typeVars.add(mQualifiedTypeName);
188 }
Florian Uunk434521c2014-04-07 17:25:11 +0100189 return false;
190 }
191
192 private String fullNameNoDimensionNoBounds(HashSet<String> typeVars) {
193 String fullName = null;
194 if (checkRecurringTypeVar(typeVars)) {
195 return mQualifiedTypeName;
196 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700197 /*
198 * if (fullName != null) { return fullName; }
199 */
200 fullName = mQualifiedTypeName;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700201 if (mTypeArguments != null && !mTypeArguments.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700202 fullName += typeArgumentsName(mTypeArguments, typeVars);
Florian Uunk434521c2014-04-07 17:25:11 +0100203 }
204 return fullName;
205 }
206
207 public String fullNameNoDimension(HashSet<String> typeVars) {
208 String fullName = null;
209 if (checkRecurringTypeVar(typeVars)) {
210 return mQualifiedTypeName;
211 }
212 fullName = fullNameNoDimensionNoBounds(typeVars);
213 if (mTypeArguments == null || mTypeArguments.isEmpty()) {
214 if (mSuperBounds != null && !mSuperBounds.isEmpty()) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700215 for (TypeInfo superBound : mSuperBounds) {
216 if (superBound == mSuperBounds.get(0)) {
Florian Uunk434521c2014-04-07 17:25:11 +0100217 fullName += " super " + superBound.fullNameNoBounds(typeVars);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700218 } else {
Florian Uunk434521c2014-04-07 17:25:11 +0100219 fullName += " & " + superBound.fullNameNoBounds(typeVars);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700220 }
221 }
Florian Uunk434521c2014-04-07 17:25:11 +0100222 } else if (mExtendsBounds != null && !mExtendsBounds.isEmpty()) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700223 for (TypeInfo extendsBound : mExtendsBounds) {
224 if (extendsBound == mExtendsBounds.get(0)) {
Florian Uunk434521c2014-04-07 17:25:11 +0100225 fullName += " extends " + extendsBound.fullNameNoBounds(typeVars);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700226 } else {
Florian Uunk434521c2014-04-07 17:25:11 +0100227 fullName += " & " + extendsBound.fullNameNoBounds(typeVars);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700228 }
229 }
Florian Uunk434521c2014-04-07 17:25:11 +0100230 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700231 }
232 return fullName;
233 }
234
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700235 public ArrayList<TypeInfo> typeArguments() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700236 return mTypeArguments;
237 }
238
239 public void makeHDF(Data data, String base) {
240 makeHDFRecursive(data, base, false, false, new HashSet<String>());
241 }
242
243 public void makeQualifiedHDF(Data data, String base) {
244 makeHDFRecursive(data, base, true, false, new HashSet<String>());
245 }
246
247 public void makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables) {
248 makeHDFRecursive(data, base, false, isLastVararg, typeVariables);
249 }
250
251 public void makeQualifiedHDF(Data data, String base, HashSet<String> typeVariables) {
252 makeHDFRecursive(data, base, true, false, typeVariables);
253 }
254
255 private void makeHDFRecursive(Data data, String base, boolean qualified, boolean isLastVararg,
256 HashSet<String> typeVars) {
257 String label = qualified ? qualifiedTypeName() : simpleTypeName();
258 label += (isLastVararg) ? "..." : dimension();
259 data.setValue(base + ".label", label);
260 if (mIsTypeVariable || mIsWildcard) {
261 // could link to an @param tag on the class to describe this
262 // but for now, just don't make it a link
263 } else if (!isPrimitive() && mClass != null) {
264 if (mClass.isIncluded()) {
265 data.setValue(base + ".link", mClass.htmlPage());
266 data.setValue(base + ".since", mClass.getSince());
267 } else {
Jeff Hamilton9c584842012-06-25 13:24:14 -0500268 Doclava.federationTagger.tag(mClass);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700269 if (!mClass.getFederatedReferences().isEmpty()) {
270 FederatedSite site = mClass.getFederatedReferences().iterator().next();
271 data.setValue(base + ".link", site.linkFor(mClass.htmlPage()));
272 data.setValue(base + ".federated", site.name());
273 }
274 }
275 }
276
277 if (mIsTypeVariable) {
278 if (typeVars.contains(qualifiedTypeName())) {
279 // don't recurse forever with the parameters. This handles
280 // Enum<K extends Enum<K>>
281 return;
282 }
283 typeVars.add(qualifiedTypeName());
284 }
285 if (mTypeArguments != null) {
286 TypeInfo.makeHDF(data, base + ".typeArguments", mTypeArguments, qualified, typeVars);
287 }
288 if (mSuperBounds != null) {
289 TypeInfo.makeHDF(data, base + ".superBounds", mSuperBounds, qualified, typeVars);
290 }
291 if (mExtendsBounds != null) {
292 TypeInfo.makeHDF(data, base + ".extendsBounds", mExtendsBounds, qualified, typeVars);
293 }
294 }
295
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700296 public static void makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified,
Ben Dodson920dbbb2010-08-04 15:21:06 -0700297 HashSet<String> typeVariables) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700298 int i = 0;
299 for (TypeInfo type : types) {
300 type.makeHDFRecursive(data, base + "." + i++, qualified, false, typeVariables);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700301 }
302 }
303
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700304 public static void makeHDF(Data data, String base, ArrayList<TypeInfo> types, boolean qualified) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700305 makeHDF(data, base, types, qualified, new HashSet<String>());
306 }
307
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700308 void setTypeArguments(ArrayList<TypeInfo> args) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700309 mTypeArguments = args;
310 }
311
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700312 public void addTypeArgument(TypeInfo arg) {
313 if (mTypeArguments == null) {
314 mTypeArguments = new ArrayList<TypeInfo>();
315 }
316
317 mTypeArguments.add(arg);
318 }
319
320 void setBounds(ArrayList<TypeInfo> superBounds, ArrayList<TypeInfo> extendsBounds) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700321 mSuperBounds = superBounds;
322 mExtendsBounds = extendsBounds;
323 }
324
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700325 public ArrayList<TypeInfo> superBounds() {
326 return mSuperBounds;
327 }
328
329 public ArrayList<TypeInfo> extendsBounds() {
330 return mExtendsBounds;
331 }
332
Ben Dodson920dbbb2010-08-04 15:21:06 -0700333 void setIsTypeVariable(boolean b) {
334 mIsTypeVariable = b;
335 }
336
337 void setIsWildcard(boolean b) {
338 mIsWildcard = b;
339 }
340
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700341 public boolean isWildcard() {
342 return mIsWildcard;
343 }
344
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700345 static HashSet<String> typeVariables(ArrayList<TypeInfo> params) {
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700346 return typeVariables(params, new HashSet<String>());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700347 }
348
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700349 static HashSet<String> typeVariables(ArrayList<TypeInfo> params, HashSet<String> result) {
350 if (params != null) {
351 for (TypeInfo t : params) {
352 if (t.mIsTypeVariable) {
353 result.add(t.mQualifiedTypeName);
354 }
355 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700356 }
357 return result;
358 }
359
360
361 public boolean isTypeVariable() {
362 return mIsTypeVariable;
363 }
364
365 public String defaultValue() {
366 if (mIsPrimitive) {
367 if ("boolean".equals(mSimpleTypeName)) {
368 return "false";
369 } else {
370 return "0";
371 }
372 } else {
373 return "null";
374 }
375 }
376
377 @Override
378 public String toString() {
379 String returnString = "";
380 returnString +=
381 "Primitive?: " + mIsPrimitive + " TypeVariable?: " + mIsTypeVariable + " Wildcard?: "
382 + mIsWildcard + " Dimension: " + mDimension + " QualifedTypeName: "
383 + mQualifiedTypeName;
384
385 if (mTypeArguments != null) {
386 returnString += "\nTypeArguments: ";
387 for (TypeInfo tA : mTypeArguments) {
388 returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
389 }
390 }
391 if (mSuperBounds != null) {
392 returnString += "\nSuperBounds: ";
393 for (TypeInfo tA : mSuperBounds) {
394 returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
395 }
396 }
397 if (mExtendsBounds != null) {
398 returnString += "\nExtendsBounds: ";
399 for (TypeInfo tA : mExtendsBounds) {
400 returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
401 }
402 }
403 return returnString;
404 }
405
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700406 public void addResolution(Resolution resolution) {
407 if (mResolutions == null) {
408 mResolutions = new ArrayList<Resolution>();
409 }
410
411 mResolutions.add(resolution);
412 }
413
414 public void printResolutions() {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700415 if (mResolutions == null || mResolutions.isEmpty()) {
416 return;
417 }
418
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700419 System.out.println("Resolutions for Type " + mSimpleTypeName + ":");
420 for (Resolution r : mResolutions) {
421 System.out.println(r);
422 }
423 }
424
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700425 public boolean resolveResolutions() {
426 ArrayList<Resolution> resolutions = mResolutions;
427 mResolutions = new ArrayList<Resolution>();
428
429 boolean allResolved = true;
430 for (Resolution resolution : resolutions) {
431 if ("class".equals(resolution.getVariable())) {
432 StringBuilder qualifiedClassName = new StringBuilder();
433 InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
434 resolution.getInfoBuilder());
435
436 // if we still couldn't resolve it, save it for the next pass
437 if ("".equals(qualifiedClassName.toString())) {
438 mResolutions.add(resolution);
439 allResolved = false;
440 } else {
441 mClass = InfoBuilder.Caches.obtainClass(qualifiedClassName.toString());
442 }
443 }
444 }
445
446 return allResolved;
447 }
448
Jeff Arnesond6570b02014-10-29 15:46:51 -0700449 /**
450 * Copy this TypeInfo, but replace type arguments with those defined in the
451 * typeArguments mapping.
452 * <p>
453 * If the current type is one of the base types in the mapping (i.e. a parameter itself)
454 * then this returns the mapped type.
455 */
456 public TypeInfo getTypeWithArguments(Map<String, TypeInfo> typeArguments) {
457 if (typeArguments.containsKey(fullName())) {
458 return typeArguments.get(fullName());
459 }
460
461 TypeInfo ti = new TypeInfo(this);
462 if (typeArguments() != null) {
463 ArrayList<TypeInfo> newArgs = new ArrayList<TypeInfo>();
464 for (TypeInfo t : typeArguments()) {
465 newArgs.add(t.getTypeWithArguments(typeArguments));
466 }
467 ti.setTypeArguments(newArgs);
468 }
469 return ti;
470 }
471
472 /**
473 * Given two TypeInfos that reference the same type, take the first one's type parameters
474 * and generate a mapping from their names to the type parameters defined in the second.
475 */
476 public static Map<String, TypeInfo> getTypeArgumentMapping(TypeInfo generic, TypeInfo typed) {
477 Map<String, TypeInfo> map = new HashMap<String, TypeInfo>();
478 for (int i = 0; i < generic.typeArguments().size(); i++) {
479 if (typed.typeArguments() != null && typed.typeArguments().size() > i) {
480 map.put(generic.typeArguments().get(i).fullName(), typed.typeArguments().get(i));
481 }
482 }
483 return map;
484 }
485
486 /**
487 * Given a ClassInfo and a parameterized TypeInfo, take the class's raw type's type parameters
488 * and generate a mapping from their names to the type parameters defined in the TypeInfo.
489 */
490 public static Map<String, TypeInfo> getTypeArgumentMapping(ClassInfo cls, TypeInfo typed) {
491 return getTypeArgumentMapping(cls.asTypeInfo(), typed);
492 }
493
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700494 private ArrayList<Resolution> mResolutions;
495
Ben Dodson920dbbb2010-08-04 15:21:06 -0700496 private boolean mIsPrimitive;
497 private boolean mIsTypeVariable;
498 private boolean mIsWildcard;
499 private String mDimension;
500 private String mSimpleTypeName;
501 private String mQualifiedTypeName;
502 private ClassInfo mClass;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700503 private ArrayList<TypeInfo> mTypeArguments;
504 private ArrayList<TypeInfo> mSuperBounds;
505 private ArrayList<TypeInfo> mExtendsBounds;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700506 private String mFullName;
507}