1 /**
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
5 * Portions Copyright 2013-2017 Philip Helger + contributors
6 *
7 * The contents of this file are subject to the terms of either the GNU
8 * General Public License Version 2 only ("GPL") or the Common Development
9 * and Distribution License("CDDL") (collectively, the "License"). You
10 * may not use this file except in compliance with the License. You can
11 * obtain a copy of the License at
12 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
13 * or packager/legal/LICENSE.txt. See the License for the specific
14 * language governing permissions and limitations under the License.
15 *
16 * When distributing the software, include this License Header Notice in each
17 * file and include the License file at packager/legal/LICENSE.txt.
18 *
19 * GPL Classpath Exception:
20 * Oracle designates this particular file as subject to the "Classpath"
21 * exception as provided by Oracle in the GPL Version 2 section of the License
22 * file that accompanied this code.
23 *
24 * Modifications:
25 * If applicable, add the following below the License Header, with the fields
26 * enclosed by brackets [] replaced by your own identifying information:
27 * "Portions Copyright [year] [name of copyright owner]"
28 *
29 * Contributor(s):
30 * If you wish your version of this file to be governed by only the CDDL or
31 * only the GPL Version 2, indicate your decision by adding "[Contributor]
32 * elects to include this software in this distribution under the [CDDL or GPL
33 * Version 2] license." If you don't indicate a single choice of license, a
34 * recipient has the option to distribute your version of this file under
35 * either the CDDL, the GPL Version 2 or to extend the choice of license to
36 * its licensees as provided above. However, if you add GPL Version 2 code
37 * and therefore, elected the GPL Version 2 license, then the option applies
38 * only if the new code is made subject to such option by the copyright
39 * holder.
40 */
41 package com.helger.jcodemodel;
42
43 import java.util.Iterator;
44
45 import javax.annotation.Nonnull;
46 import javax.annotation.Nullable;
47
48 /**
49 * A representation of a type in codeModel. A type is always either primitive (
50 * {@link JPrimitiveType}) or a reference type ({@link AbstractJClass}).<br>
51 * Note: up to version 2.7.6 this class implemented
52 * <code>Comparable <AbstractJType></code>. Since this was specific to
53 * import handling on emitting code, it was removed with 2.7.7!
54 */
55 public abstract class AbstractJType implements IJGenerable, IJOwned
56 {
57 /**
58 * Obtains a reference to the primitive type object from a type name.
59 *
60 * @param aCodeModel
61 * Base code model
62 * @param sTypeName
63 * primitive type to be parsed (e.g. "int" or "void")
64 * @return Never <code>null</code>
65 * @throws IllegalArgumentException
66 * If the passed type name is not a primitive type name
67 */
68 @Nonnull
69 public static JPrimitiveType parse (@Nonnull final JCodeModel aCodeModel, @Nonnull final String sTypeName)
70 {
71 if (sTypeName.equals ("void"))
72 return aCodeModel.VOID;
73 if (sTypeName.equals ("boolean"))
74 return aCodeModel.BOOLEAN;
75 if (sTypeName.equals ("byte"))
76 return aCodeModel.BYTE;
77 if (sTypeName.equals ("short"))
78 return aCodeModel.SHORT;
79 if (sTypeName.equals ("char"))
80 return aCodeModel.CHAR;
81 if (sTypeName.equals ("int"))
82 return aCodeModel.INT;
83 if (sTypeName.equals ("float"))
84 return aCodeModel.FLOAT;
85 if (sTypeName.equals ("long"))
86 return aCodeModel.LONG;
87 if (sTypeName.equals ("double"))
88 return aCodeModel.DOUBLE;
89 throw new IllegalArgumentException ("Not a primitive type: " + sTypeName);
90 }
91
92 /**
93 * Gets the full name of the type. See
94 * http://java.sun.com/docs/books/jls/second_edition/html/names.doc.html#25430
95 * for the details.
96 *
97 * @return Strings like "int", "java.lang.String", "java.io.File[]". May be
98 * <code>null</code> for unnamed classes.
99 */
100 @Nullable
101 public abstract String fullName ();
102
103 /**
104 * Gets the binary name of the type. See
105 * http://java.sun.com/docs/books/jls/third_edition/html/binaryComp.html#44909
106 *
107 * @return Name like "Foo$Bar", "int", "java.lang.String", "java.io.File[]".
108 * Never null.
109 */
110 @Nonnull
111 public String binaryName ()
112 {
113 return fullName ();
114 }
115
116 /**
117 * Gets the name of this type.
118 *
119 * @return Names like "int", "void", "BigInteger".
120 */
121 @Nonnull
122 public abstract String name ();
123
124 /**
125 * Create an array type of this type. This method is undefined for primitive
126 * void type, which doesn't have any corresponding array representation.
127 *
128 * @return A {@link JArrayClass} representing the array type whose element
129 * type is this type
130 */
131 public abstract JArrayClass array ();
132
133 /**
134 * Tell whether or not this is an array type.
135 *
136 * @return <code>true</code> if this an array type
137 */
138 public boolean isArray ()
139 {
140 return false;
141 }
142
143 /**
144 * Tell whether or not this is a built-in primitive type, such as int or void.
145 *
146 * @return <code>true</code> if this is a primitive type
147 * @see #isReference()
148 */
149 public boolean isPrimitive ()
150 {
151 return false;
152 }
153
154 /**
155 * @return <code>true</code> if this is a reference type (which means it is
156 * not a primitive type).
157 * @see #isPrimitive()
158 */
159 public final boolean isReference ()
160 {
161 return !isPrimitive ();
162 }
163
164 /**
165 * Tells whether or not this is an error-type.
166 * <p>
167 * Error types are not actual Java types and shouldn't be used in actually
168 * generated code.
169 *
170 * @return <code>true</code> if this is an error class
171 * @see JErrorClass
172 */
173 public boolean isError ()
174 {
175 return false;
176 }
177
178 /**
179 * If this class is a primitive type, return the boxed class. Otherwise return
180 * <tt>this</tt>.
181 * <p>
182 * For example, for "int", this method returns "java.lang.Integer".
183 *
184 * @return Never <code>null</code>
185 */
186 @Nonnull
187 public abstract AbstractJClass boxify ();
188
189 /**
190 * If this class is a wrapper type for a primitive, return the primitive type.
191 * Otherwise return <tt>this</tt>.
192 * <p>
193 * For example, for "java.lang.Integer", this method returns "int".
194 *
195 * @return Never <code>null</code>
196 */
197 @Nonnull
198 public abstract AbstractJType unboxify ();
199
200 /**
201 * @return the erasure of this type. This is only relevant for narrowed
202 * classes like <code>List<Integer></code> in which case this
203 * method returns the reference to <code>List</code> without any
204 * generic type parameters.
205 */
206 @Nonnull
207 public AbstractJType erasure ()
208 {
209 return this;
210 }
211
212 /**
213 * @return type that can be used to declare method result or variable. This is
214 * only relevant for wildcards types that can't be used in such
215 * context in which case this method returns the bound of wildcard
216 * type.
217 */
218 @Nonnull
219 public AbstractJType declarable ()
220 {
221 return this;
222 }
223
224 /**
225 * If this is an array, returns the component type of the array (T of T[]).
226 * Important: call this method only if you check that this is an array type (
227 * {@link #isArray()}).
228 *
229 * @return Never <code>null</code>.
230 * @throws IllegalArgumentException
231 * If this is not an array type
232 */
233 @Nonnull
234 public AbstractJType elementType ()
235 {
236 throw new IllegalArgumentException ("Not an array type: " + fullName ());
237 }
238
239 /**
240 * Check if this class is a generic class and contains the passed type
241 * variable.
242 *
243 * @param aVar
244 * The type variable to check. May be <code>null</code>.
245 * @return <code>true</code> if the passed type variable is contained,
246 * <code>false</code> otherwise.
247 */
248 public boolean containsTypeVar (@Nullable final JTypeVar aVar)
249 {
250 return false;
251 }
252
253 /**
254 * Checks the relationship between two types.
255 * <p>
256 * This method performs superset of actions that are performed by
257 * {@link Class#isAssignableFrom(Class)} For example,
258 * baseClass.isAssignableFrom(derivedClass) is always <code>true</code>.
259 * <p>
260 * There are two differences of this method and
261 * {@link Class#isAssignableFrom(Class)}
262 * <ol>
263 * <li>This method works with primitive types
264 * <li>This method processes generic arguments and supports wildcards
265 * </ol>
266 * <p>
267 * Examples:
268 * <ol>
269 * <li>[[List]].isAssignableFrom ([[List<T>]])</li>
270 * <li>[[List<T>]].isAssignableFrom ([[List]])</li>
271 * <li>[[List<? extends Object>]].isAssignableFrom
272 * ([[List<Integer>]])</li>
273 * <li>[[List<? super Serializable>]].isAssignableFrom
274 * ([[List<String>]])</li>
275 * <li>[[List<? super Serializable>]].isAssignableFrom
276 * ([[List<String>]])</li>
277 * <li>[[List<? extends Object>]].isAssignableFrom ([[List<? extends
278 * Integer>]])</li>
279 * <li>[[List<? extends List<? extends Object>>]].isAssignableFrom
280 * ([[List<List<Integer>>]])</li>
281 * </ol>
282 *
283 * @param aThat
284 * Type to check
285 * @return <code>true</code> if assignable, <code>false</code> if not
286 */
287 public boolean isAssignableFrom (@Nonnull final AbstractJType aThat)
288 {
289 return isAssignableFrom (aThat, true);
290 }
291
292 protected boolean isAssignableFrom (@Nonnull final AbstractJType aThat, final boolean bAllowsRawTypeUnchekedConversion)
293 {
294 if (isError () || aThat.isError ())
295 return false;
296 if (this.equals (aThat))
297 return true;
298
299 if (this.isReference () && aThat.isReference ())
300 {
301 final AbstractJClass thisClass = (AbstractJClass) this;
302 final AbstractJClass thatClass = (AbstractJClass) aThat;
303
304 // Bottom: Anything anything = null
305 if (thatClass instanceof JNullType)
306 return true;
307
308 // Top: Object object = (Anything)anything
309 if (thisClass == thisClass._package ().owner ().ref (Object.class))
310 return true;
311
312 // Array covariance: i. e. Object[] array1 = (Integer[])array2
313 if (this.isArray () && aThat.isArray ())
314 return this.elementType ().isAssignableFrom (aThat.elementType (), false);
315
316 if (thisClass.erasure ().equals (thatClass.erasure ()))
317 {
318 // Raw classes: i. e. List list1 = (List<T>)list2;
319 if (!thisClass.isParameterized ())
320 return true;
321
322 // Raw classes unchecked conversion: i. e. List<T> list1 = (List)list2
323 if (!thatClass.isParameterized ())
324 return bAllowsRawTypeUnchekedConversion;
325
326 for (int i = 0; i < thisClass.getTypeParameters ().size (); i++)
327 {
328 final AbstractJClass thisParameter = thisClass.getTypeParameters ().get (i);
329 final AbstractJClass thatParameter = thatClass.getTypeParameters ().get (i);
330
331 if (thisParameter instanceof JTypeWildcard)
332 {
333 final JTypeWildcard thisWildcard = (JTypeWildcard) thisParameter;
334
335 if (thatParameter instanceof JTypeWildcard)
336 {
337 final JTypeWildcard thatWildcard = (JTypeWildcard) thatParameter;
338 if (thisWildcard.boundMode () != thatWildcard.boundMode ())
339 return false;
340 if (thisWildcard.boundMode () == EWildcardBoundMode.EXTENDS)
341 return thisWildcard.bound ().isAssignableFrom (thatWildcard.bound (), false);
342 if (thisWildcard.boundMode () == EWildcardBoundMode.SUPER)
343 return thatWildcard.bound ().isAssignableFrom (thisWildcard.bound (), false);
344 throw new IllegalStateException ("Unsupported wildcard bound mode: " + thisWildcard.boundMode ());
345 }
346
347 if (thisWildcard.boundMode () == EWildcardBoundMode.EXTENDS)
348 return thisWildcard.bound ().isAssignableFrom (thatParameter, false);
349 if (thisWildcard.boundMode () == EWildcardBoundMode.SUPER)
350 return thatParameter.isAssignableFrom (thisWildcard.bound (), false);
351 throw new IllegalStateException ("Unsupported wildcard bound mode: " + thisWildcard.boundMode ());
352 }
353
354 if (!thisParameter.equals (thatParameter))
355 return false;
356 }
357 return true;
358 }
359
360 final AbstractJClass thatClassBase = thatClass._extends ();
361 if (thatClassBase != null && this.isAssignableFrom (thatClassBase))
362 return true;
363
364 final Iterator <AbstractJClass> i = thatClass._implements ();
365 while (i.hasNext ())
366 {
367 final AbstractJClass thatClassInterface = i.next ();
368 if (this.isAssignableFrom (thatClassInterface))
369 return true;
370 }
371 // false so far
372 }
373
374 return false;
375 }
376
377 @Override
378 public String toString ()
379 {
380 return this.getClass ().getName () + '(' + fullName () + ')';
381 }
382 }