View Javadoc
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 &lt;AbstractJType&gt;</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&lt;Integer&gt;</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&lt;T&gt;]])</li>
270    * <li>[[List&lt;T&gt;]].isAssignableFrom ([[List]])</li>
271    * <li>[[List&lt;? extends Object&gt;]].isAssignableFrom
272    * ([[List&lt;Integer&gt;]])</li>
273    * <li>[[List&lt;? super Serializable&gt;]].isAssignableFrom
274    * ([[List&lt;String&gt;]])</li>
275    * <li>[[List&lt;? super Serializable&gt;]].isAssignableFrom
276    * ([[List&lt;String&gt;]])</li>
277    * <li>[[List&lt;? extends Object&gt;]].isAssignableFrom ([[List&lt;? extends
278    * Integer&gt;]])</li>
279    * <li>[[List&lt;? extends List&lt;? extends Object&gt;&gt;]].isAssignableFrom
280    * ([[List&lt;List&lt;Integer&gt;&gt;]])</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 }