1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.generator.api.dom.java;
17
18 import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
19 import static org.mybatis.generator.internal.util.messages.Messages.getString;
20
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.StringTokenizer;
25 import java.util.stream.Collectors;
26
27 import org.jspecify.annotations.Nullable;
28 import org.mybatis.generator.exception.TypeParsingException;
29
30 public class FullyQualifiedJavaType implements Comparable<FullyQualifiedJavaType> {
31
32 private static final String JAVA_LANG = "java.lang";
33
34 private static @Nullable FullyQualifiedJavaType intInstance = null;
35
36 private static @Nullable FullyQualifiedJavaType stringInstance = null;
37
38 private static @Nullable FullyQualifiedJavaType booleanPrimitiveInstance = null;
39
40 private static @Nullable FullyQualifiedJavaType objectInstance = null;
41
42 private static @Nullable FullyQualifiedJavaType dateInstance = null;
43
44 private static @Nullable FullyQualifiedJavaType criteriaInstance = null;
45
46 private static @Nullable FullyQualifiedJavaType generatedCriteriaInstance = null;
47
48
49 private String baseShortName = "";
50
51
52 private String baseQualifiedName = "";
53
54 private boolean explicitlyImported;
55
56 private String packageName = "";
57
58 private boolean primitive;
59
60 private boolean isArray;
61
62 private @Nullable PrimitiveTypeWrapper primitiveTypeWrapper;
63
64 private final List<FullyQualifiedJavaType> typeArguments;
65
66
67 private boolean wildcardType;
68
69 private boolean boundedWildcard;
70
71 private boolean extendsBoundedWildcard;
72
73
74
75
76
77
78
79 public FullyQualifiedJavaType(String fullTypeSpecification) {
80 super();
81 typeArguments = new ArrayList<>();
82 parse(fullTypeSpecification);
83 }
84
85 public boolean isExplicitlyImported() {
86 return explicitlyImported;
87 }
88
89
90
91
92
93
94 public String getFullyQualifiedName() {
95 String s = getFullyQualifiedNameWithoutTypeParameters();
96
97 if (typeArguments.isEmpty()) {
98 return s;
99 } else {
100 return typeArguments.stream()
101 .map(FullyQualifiedJavaType::getFullyQualifiedName)
102 .collect(Collectors.joining(", ", s + "<", ">"));
103 }
104 }
105
106 public String getFullyQualifiedNameWithoutTypeParameters() {
107 return calculateBaseType(baseQualifiedName);
108 }
109
110
111
112
113
114
115 public String getImportName() {
116 return baseQualifiedName;
117 }
118
119
120
121
122
123
124
125 public List<String> getImportList() {
126 List<String> answer = new ArrayList<>();
127 if (isExplicitlyImported()) {
128 int index = baseShortName.indexOf('.');
129 if (index == -1) {
130 answer.add(calculateActualImport(baseQualifiedName));
131 } else {
132
133
134 String sb = packageName + '.' + calculateActualImport(baseShortName.substring(0, index));
135 answer.add(sb);
136 }
137 }
138
139 typeArguments.forEach(t -> answer.addAll(t.getImportList()));
140
141 return answer;
142 }
143
144 private String calculateActualImport(String name) {
145 String answer = name;
146 if (this.isArray()) {
147 int index = name.indexOf('[');
148 if (index != -1) {
149 answer = name.substring(0, index);
150 }
151 }
152 return answer;
153 }
154
155 public String getPackageName() {
156 return packageName;
157 }
158
159 public String getShortName() {
160 String s = getShortNameWithoutTypeArguments();
161
162 if (typeArguments.isEmpty()) {
163 return s;
164 } else {
165 return typeArguments.stream()
166 .map(FullyQualifiedJavaType::getShortName)
167 .collect(Collectors.joining(", ", s + "<", ">"));
168 }
169 }
170
171 public String getShortNameWithoutTypeArguments() {
172 return calculateBaseType(baseShortName);
173 }
174
175 private String calculateBaseType(String name) {
176 StringBuilder sb = new StringBuilder();
177 if (wildcardType) {
178 sb.append('?');
179 if (boundedWildcard) {
180 if (extendsBoundedWildcard) {
181 sb.append(" extends ");
182 } else {
183 sb.append(" super ");
184 }
185
186 sb.append(name);
187 }
188 } else {
189 sb.append(name);
190 }
191 return sb.toString();
192 }
193
194 @Override
195 public boolean equals(Object obj) {
196 if (this == obj) {
197 return true;
198 }
199
200 if (!(obj instanceof FullyQualifiedJavaType other)) {
201 return false;
202 }
203
204 return getFullyQualifiedName().equals(other.getFullyQualifiedName());
205 }
206
207 @Override
208 public int hashCode() {
209 return getFullyQualifiedName().hashCode();
210 }
211
212 @Override
213 public String toString() {
214 return getFullyQualifiedName();
215 }
216
217 public boolean isPrimitive() {
218 return primitive;
219 }
220
221 public PrimitiveTypeWrapper getPrimitiveTypeWrapper() {
222 return Objects.requireNonNull(primitiveTypeWrapper);
223 }
224
225 public static FullyQualifiedJavaType getIntInstance() {
226 if (intInstance == null) {
227 intInstance = new FullyQualifiedJavaType("int");
228 }
229
230 return intInstance;
231 }
232
233 public static FullyQualifiedJavaType getNewListInstance() {
234
235 return new FullyQualifiedJavaType("java.util.List");
236 }
237
238 public static FullyQualifiedJavaType getNewHashMapInstance() {
239
240 return new FullyQualifiedJavaType("java.util.HashMap");
241 }
242
243 public static FullyQualifiedJavaType getNewArrayListInstance() {
244
245 return new FullyQualifiedJavaType("java.util.ArrayList");
246 }
247
248 public static FullyQualifiedJavaType getNewIteratorInstance() {
249
250 return new FullyQualifiedJavaType("java.util.Iterator");
251 }
252
253 public static FullyQualifiedJavaType getStringInstance() {
254 stringInstance = Objects.requireNonNullElseGet(stringInstance,
255 () -> new FullyQualifiedJavaType("java.lang.String"));
256 return stringInstance;
257 }
258
259 public static FullyQualifiedJavaType getBooleanPrimitiveInstance() {
260 booleanPrimitiveInstance = Objects.requireNonNullElseGet(booleanPrimitiveInstance,
261 () -> new FullyQualifiedJavaType("boolean"));
262 return booleanPrimitiveInstance;
263 }
264
265 public static FullyQualifiedJavaType getObjectInstance() {
266 objectInstance = Objects.requireNonNullElseGet(objectInstance,
267 () -> new FullyQualifiedJavaType("java.lang.Object"));
268 return objectInstance;
269 }
270
271 public static FullyQualifiedJavaType getDateInstance() {
272 dateInstance = Objects.requireNonNullElseGet(dateInstance,
273 () -> new FullyQualifiedJavaType("java.util.Date"));
274 return dateInstance;
275 }
276
277 public static FullyQualifiedJavaType getCriteriaInstance() {
278 criteriaInstance = Objects.requireNonNullElseGet(criteriaInstance,
279 () -> new FullyQualifiedJavaType("Criteria"));
280 return criteriaInstance;
281 }
282
283 public static FullyQualifiedJavaType getGeneratedCriteriaInstance() {
284 generatedCriteriaInstance = Objects.requireNonNullElseGet(generatedCriteriaInstance,
285 () -> new FullyQualifiedJavaType("GeneratedCriteria"));
286 return generatedCriteriaInstance;
287 }
288
289 @Override
290 public int compareTo(FullyQualifiedJavaType other) {
291 return getFullyQualifiedName().compareTo(other.getFullyQualifiedName());
292 }
293
294 public void addTypeArgument(FullyQualifiedJavaType type) {
295 typeArguments.add(type);
296 }
297
298 private void parse(String fullTypeSpecification) {
299 String spec = fullTypeSpecification.trim();
300
301 if (spec.startsWith("?")) {
302 wildcardType = true;
303 spec = spec.substring(1).trim();
304 if (spec.startsWith("extends ")) {
305 boundedWildcard = true;
306 extendsBoundedWildcard = true;
307 spec = spec.substring(8);
308 } else if (spec.startsWith("super ")) {
309 boundedWildcard = true;
310 extendsBoundedWildcard = false;
311 spec = spec.substring(6);
312 } else {
313 boundedWildcard = false;
314 }
315 parse(spec);
316 } else {
317 int index = fullTypeSpecification.indexOf('<');
318 if (index == -1) {
319 simpleParse(fullTypeSpecification);
320 } else {
321 simpleParse(fullTypeSpecification.substring(0, index));
322 int endIndex = fullTypeSpecification.lastIndexOf('>');
323 if (endIndex == -1) {
324 throw new TypeParsingException(getString("RuntimeError.22", fullTypeSpecification));
325 }
326 genericParse(fullTypeSpecification.substring(index, endIndex + 1));
327 }
328
329
330
331
332
333 isArray = fullTypeSpecification.endsWith("]");
334 }
335 }
336
337 private void simpleParse(String typeSpecification) {
338 baseQualifiedName = typeSpecification.trim();
339 if (baseQualifiedName.contains(".")) {
340 packageName = getPackage(baseQualifiedName);
341 baseShortName = baseQualifiedName.substring(packageName.length() + 1);
342 int index = baseShortName.lastIndexOf('.');
343 if (index != -1) {
344 baseShortName = baseShortName.substring(index + 1);
345 }
346
347
348 explicitlyImported = !JAVA_LANG.equals(packageName);
349 } else {
350 baseShortName = baseQualifiedName;
351 explicitlyImported = false;
352 packageName = "";
353
354 switch (baseQualifiedName) {
355 case "byte":
356 primitive = true;
357 primitiveTypeWrapper = PrimitiveTypeWrapper.getByteInstance();
358 break;
359 case "short":
360 primitive = true;
361 primitiveTypeWrapper = PrimitiveTypeWrapper.getShortInstance();
362 break;
363 case "int":
364 primitive = true;
365 primitiveTypeWrapper = PrimitiveTypeWrapper.getIntegerInstance();
366 break;
367 case "long":
368 primitive = true;
369 primitiveTypeWrapper = PrimitiveTypeWrapper.getLongInstance();
370 break;
371 case "char":
372 primitive = true;
373 primitiveTypeWrapper = PrimitiveTypeWrapper.getCharacterInstance();
374 break;
375 case "float":
376 primitive = true;
377 primitiveTypeWrapper = PrimitiveTypeWrapper.getFloatInstance();
378 break;
379 case "double":
380 primitive = true;
381 primitiveTypeWrapper = PrimitiveTypeWrapper.getDoubleInstance();
382 break;
383 case "boolean":
384 primitive = true;
385 primitiveTypeWrapper = PrimitiveTypeWrapper.getBooleanInstance();
386 break;
387 default:
388 primitive = false;
389 primitiveTypeWrapper = null;
390 break;
391 }
392 }
393 }
394
395 private void genericParse(String genericSpecification) {
396 int lastIndex = genericSpecification.lastIndexOf('>');
397 if (lastIndex == -1) {
398
399 throw new TypeParsingException(getString("RuntimeError.22", genericSpecification));
400 }
401 String argumentString = genericSpecification.substring(1, lastIndex);
402
403 StringTokenizer st = new StringTokenizer(argumentString, ",<>", true);
404 int openCount = 0;
405 StringBuilder sb = new StringBuilder();
406 while (st.hasMoreTokens()) {
407 String token = st.nextToken();
408 if ("<".equals(token)) {
409 sb.append(token);
410 openCount++;
411 } else if (">".equals(token)) {
412 sb.append(token);
413 openCount--;
414 } else if (",".equals(token)) {
415 if (openCount == 0) {
416 typeArguments
417 .add(new FullyQualifiedJavaType(sb.toString()));
418 sb.setLength(0);
419 } else {
420 sb.append(token);
421 }
422 } else {
423 sb.append(token);
424 }
425 }
426
427 if (openCount != 0) {
428 throw new TypeParsingException(getString("RuntimeError.22", genericSpecification));
429 }
430
431 String finalType = sb.toString();
432 if (stringHasValue(finalType)) {
433 typeArguments.add(new FullyQualifiedJavaType(finalType));
434 }
435 }
436
437
438
439
440
441
442
443
444
445
446
447
448
449 private static String getPackage(String baseQualifiedName) {
450 int index = baseQualifiedName.lastIndexOf('.');
451 return baseQualifiedName.substring(0, index);
452 }
453
454 public boolean isArray() {
455 return isArray;
456 }
457
458 public List<FullyQualifiedJavaType> getTypeArguments() {
459 return typeArguments;
460 }
461 }