/******************************************************************************* * Copyright (c) 2006, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation * ******************************************************************************/ package org.eclipse.persistence.jpa.tests.jpql.tools.spi.java; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.List; import org.eclipse.persistence.jpa.jpql.tools.spi.IType; import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration; import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeRepository; /** * The concrete implementation of {@link ITypeDeclaration} that is wrapping the representation * of the declaration description of a type. * * @see org.eclipse.persistence.jpa.jpql.tools.spi.IMapping IMapping * @see IType * * @version 2.4 * @since 2.3 * @author Pascal Filion */ @SuppressWarnings("nls") public class JavaTypeDeclaration implements ITypeDeclaration { /** * Determines whether the type declaration represents an array. */ private boolean array; /** * The actual type that contains the generics, if any is present. */ private Object genericType; /** * The cached {@link ITypeDeclaration ITypeDeclarations} representing the generics of the {@link * Type}. */ private ITypeDeclaration[] genericTypes; /** * The external form of the Java type. */ private final IType type; /** * The repository of {@link IType ITypes}. */ private ITypeRepository typeRepository; /** * Creates a new <code>JavaTypeDeclaration</code>. * * @param typeRepository The repository of {@link IType ITypes} * @param type The external form of the Java type * @param genericType The actual type that contains the generics, if any is present * @param array Determines whether the type declaration represents an array */ public JavaTypeDeclaration(ITypeRepository typeRepository, IType type, Object genericType, boolean array) { super(); this.type = type; this.array = array; this.genericType = genericType; this.typeRepository = typeRepository; } protected String buildArrayTypeName(String arrayTypeName) { StringBuilder sb = new StringBuilder(); int index = arrayTypeName.indexOf('['); int dimensionality = (arrayTypeName.length() - index) / 2; String typeName = arrayTypeName.substring(0, index); while (--dimensionality >= 0) { sb.append("["); } String elementType = elementType(typeName); sb.append(elementType); sb.append(typeName); if (elementType.equals("L")) { sb.append(";"); } return sb.toString(); } protected ITypeDeclaration[] buildParameterTypes() { List<ITypeDeclaration> parameterTypes = new ArrayList<ITypeDeclaration>(); // Example: Class<T> if (genericType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericType; for (Type type : parameterizedType.getActualTypeArguments()) { ITypeDeclaration typeParameter = buildTypeDeclaration(type); parameterTypes.add(typeParameter); } } // T[] else if (genericType instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) genericType; parameterTypes.add(buildTypeDeclaration(genericArrayType.getGenericComponentType())); } // Example: Class else if (genericType.getClass() == Class.class) { ITypeDeclaration typeParameter = buildTypeDeclaration((Class<?>) genericType); parameterTypes.add(typeParameter); } // Example: <K, V> else if (genericType.getClass() == Class[].class) { for (Class<?> javaType : ((Class<?>[]) genericType)) { ITypeDeclaration typeParameter = buildTypeDeclaration(javaType); parameterTypes.add(typeParameter); } } // Example: <K, V> else if (genericType.getClass() == IType[].class) { for (IType type : ((IType[]) genericType)) { ITypeDeclaration typeParameter = new JavaTypeDeclaration(typeRepository, type, null, false); parameterTypes.add(typeParameter); } } return parameterTypes.toArray(new ITypeDeclaration[parameterTypes.size()]); } protected JavaTypeDeclaration buildTypeDeclaration(Class<?> javaType) { return new JavaTypeDeclaration( typeRepository, getType(javaType), null, javaType.isArray() ); } protected JavaTypeDeclaration buildTypeDeclaration(Object genericType) { // <T1, ..., Tn> if (genericType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericType; return buildTypeDeclaration(parameterizedType.getRawType()); } // <T> if (genericType instanceof TypeVariable) { TypeVariable<?> typeVariable = (TypeVariable<?>) genericType; for (Type type : typeVariable.getBounds()) { return buildTypeDeclaration(type); } return buildTypeDeclaration(Object.class); } // ? if (genericType instanceof WildcardType) { WildcardType wildcardType = (WildcardType) genericType; for (Type type : wildcardType.getUpperBounds()) { return buildTypeDeclaration(type); } return buildTypeDeclaration(Object.class); } // T[] if (genericType instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) genericType; String arrayTypeName = buildArrayTypeName(genericArrayType.toString()); IType arrayType = typeRepository.getType(arrayTypeName); return new JavaTypeDeclaration( typeRepository, arrayType, genericArrayType.getGenericComponentType(), true ); } return buildTypeDeclaration((Class<?>) genericType); } protected String elementType(String typeName) { if (typeName.equals("boolean")) return "Z"; if (typeName.equals("byte")) return "B"; if (typeName.equals("char")) return "C"; if (typeName.equals("double")) return "D"; if (typeName.equals("float")) return "F"; if (typeName.equals("int")) return "I"; if (typeName.equals("long")) return "J"; if (typeName.equals("short")) return "S"; return "L"; } /** * {@inheritDoc} */ public int getDimensionality() { if (array) { String name = type.getName(); int index = 0; while (name.charAt(index) == '[') { index++; } return index; } return 0; } /** * {@inheritDoc} */ public IType getType() { return type; } protected IType getType(Class<?> type) { return typeRepository.getType(type); } /** * {@inheritDoc} */ public ITypeDeclaration[] getTypeParameters() { if (genericTypes == null) { if (genericType == null) { genericTypes = new ITypeDeclaration[0]; } else { genericTypes = buildParameterTypes(); } } return genericTypes; } /** * {@inheritDoc} */ public boolean isArray() { return array; } /** * {@inheritDoc} */ @Override public String toString() { if (genericType == null) { return type.toString(); } if (genericType instanceof Class<?>) { return ((Class<?>) genericType).getName(); } return genericType.toString(); } }