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 }