| /******************************************************************************* |
| * Copyright (c) 2011, 2012 Oracle. 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.jpt.jpa.core.jpql.spi; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jpt.common.utility.internal.StringTools; |
| import org.eclipse.persistence.jpa.jpql.TypeHelper; |
| import org.eclipse.persistence.jpa.jpql.spi.ITypeDeclaration; |
| import org.eclipse.persistence.jpa.jpql.spi.ITypeRepository; |
| |
| /** |
| * The concrete implementation of {@link ITypeRepository} that is wrapping the design-time |
| * representation of a type repository. |
| * |
| * Provisional API: This interface is part of an interim API that is still under development and |
| * expected to change significantly before reaching stability. It is available at this early stage |
| * to solicit feedback from pioneering adopters on the understanding that any code that uses this |
| * API will almost certainly be broken (repeatedly) as the API evolves. |
| * |
| * @version 3.2 |
| * @since 3.0 |
| * @author Pascal Filion |
| */ |
| @SuppressWarnings("nls") |
| public class JpaTypeRepository implements ITypeRepository { |
| |
| /** |
| * The Java project that gives access the design-time objects representing the Java types. |
| */ |
| private final IJavaProject javaProject; |
| |
| /** |
| * Keeps a map of the primitives for fast access. |
| */ |
| private Map<String, Class<?>> primitives; |
| |
| /** |
| * The cached helper that is using this repository for retrieving {@link IJpaType types}. |
| */ |
| private TypeHelper typeHelper; |
| |
| /** |
| * The types that have been cached for faster access. |
| */ |
| private final Map<String, IJpaType> types; |
| |
| /** |
| * The {@link IJpaType} that represents a unresolvable or simply an unknown type, which is created |
| * when {@link #getType(String)} is invoked with {@link IJpaType#UNRESOLVABLE_TYPE}. |
| */ |
| private IJpaType unresolvableType; |
| |
| /** |
| * Creates a new <code>JpaTypeRepository</code>. |
| * |
| * @param javaProject The Java project that gives access the design-time objects representing |
| * the Java types |
| */ |
| public JpaTypeRepository(IJavaProject javaProject) { |
| super(); |
| this.javaProject = javaProject; |
| this.types = new HashMap<String, IJpaType>(); |
| } |
| |
| protected String buildInnerTypeName(String typeName, int index) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(typeName.substring(0, index)); |
| sb.append("$"); |
| sb.append(typeName.substring(index + 1, typeName.length())); |
| return sb.toString(); |
| } |
| |
| protected Map<String, Class<?>> buildPrimitives() { |
| Map<String, Class<?>> primitives = new HashMap<String, Class<?>>(); |
| primitives.put(Byte .TYPE.getName(), Byte .TYPE); |
| primitives.put(Short .TYPE.getName(), Short .TYPE); |
| primitives.put(Character.TYPE.getName(), Character.TYPE); |
| primitives.put(Integer .TYPE.getName(), Integer .TYPE); |
| primitives.put(Long .TYPE.getName(), Long .TYPE); |
| primitives.put(Float .TYPE.getName(), Float .TYPE); |
| primitives.put(Double .TYPE.getName(), Double .TYPE); |
| primitives.put(Boolean .TYPE.getName(), Boolean .TYPE); |
| return primitives; |
| } |
| |
| protected IJpaType buildType(Class<?> javaType) { |
| JavaType jpaType = new JavaType(this, javaType); |
| types.put(jpaType.getName(), jpaType); |
| return jpaType; |
| } |
| |
| protected IJpaType buildType(IType type) { |
| JpaType jpaType = new JpaType(this, type); |
| types.put(jpaType.getName(), jpaType); |
| return jpaType; |
| } |
| |
| protected IJpaType buildType(String typeName) { |
| IJpaType jpaType = new SimpleType(this, typeName); |
| types.put(typeName, jpaType); |
| return jpaType; |
| } |
| |
| protected String convertToJavaArrayType(String typeName) { |
| |
| int index = typeName.indexOf("[]"); |
| int dimensionality = (typeName.length() - index) / 2; |
| StringBuilder sb = new StringBuilder(); |
| |
| while (dimensionality-- > 0) { |
| sb.append("["); |
| } |
| |
| String type = typeName.substring(0, index); |
| Class<?> primitive = primitives.get(type); |
| |
| if (primitive != null) { |
| if (primitive == Byte.TYPE) { |
| sb.append("B"); |
| } |
| else if (primitive == Character.TYPE) { |
| sb.append("C"); |
| } |
| else if (primitive == Short.TYPE) { |
| sb.append("S"); |
| } |
| else if (primitive == Integer.TYPE) { |
| sb.append("I"); |
| } |
| else if (primitive == Long.TYPE) { |
| sb.append("L"); |
| } |
| else if (primitive == Float.TYPE) { |
| sb.append("F"); |
| } |
| else if (primitive == Double.TYPE) { |
| sb.append("D"); |
| } |
| else if (primitive == Boolean.TYPE) { |
| sb.append("Z"); |
| } |
| } |
| else { |
| sb.append(type); |
| sb.append(";"); |
| } |
| |
| return sb.toString(); |
| } |
| |
| protected Class<?> findPrimitive(String typeName) { |
| if (primitives == null) { |
| primitives = buildPrimitives(); |
| } |
| return primitives.get(typeName); |
| } |
| |
| /** |
| * Retrieves the design-time Java type for the given type name, which has to be the fully |
| * qualified type name. |
| * |
| * @param typeName The fully qualified type name |
| * @return The design-time Java type if it could be retrieved; <code>null</code> otherwise |
| */ |
| protected IType findType(String typeName) { |
| try { |
| return javaProject.findType(typeName); |
| } |
| catch (JavaModelException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public IJpaType getEnumType(String enumTypeName) { |
| |
| // Make sure the enum type name is not empty before doing operations over it |
| if (StringTools.stringIsEmpty(enumTypeName)) { |
| return null; |
| } |
| |
| // Get the position of the last dot so the enum constant can be removed |
| int lastDotIndex = enumTypeName.lastIndexOf("."); |
| |
| if (lastDotIndex == -1) { |
| return null; |
| } |
| |
| // Retrieve the fully qualified enum type name |
| String typeName = enumTypeName.substring(0, lastDotIndex); |
| |
| // Attempt to load the enum type |
| IType type = findType(typeName); |
| IJpaType jpaType; |
| |
| if (type != null) { |
| jpaType = buildType(type); |
| } |
| else { |
| jpaType = loadInnerType(typeName); |
| } |
| |
| return (jpaType != null) && jpaType.isEnum() ? jpaType : null; |
| } |
| |
| /** |
| * Returns the Java project, which gives access to the class path. |
| * |
| * @return The Java project, which gives access to the class path |
| */ |
| protected IJavaProject getJavaProject() { |
| return javaProject; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public IJpaType getType(Class<?> javaClass) { |
| IJpaType type = types.get(javaClass.getName()); |
| if (type == null) { |
| type = buildType(javaClass); |
| } |
| return type; |
| } |
| |
| /** |
| * Retrieves the {@link IType} for the given {@link IResource}. |
| * |
| * @param resource The workspace location of the {@link IType} to retrieve |
| * @return The design-time representation of a Java type |
| */ |
| protected IType getType(IResource resource) { |
| try { |
| return (IType) javaProject.findElement((IPath) resource); |
| } |
| catch (Exception e) { |
| return null; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public IJpaType getType(String typeName) { |
| |
| if (StringTools.stringIsEmpty(typeName) || |
| IJpaType.UNRESOLVABLE_TYPE == typeName) { |
| |
| return unresolvableType(); |
| } |
| |
| if (typeName.endsWith("[]")) { |
| return loadArrayType(typeName); |
| } |
| |
| if (typeName.charAt(0) == '[') { |
| return loadJavaArrayType(typeName); |
| } |
| |
| return loadTypeImp(typeName); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public TypeHelper getTypeHelper() { |
| if (typeHelper == null) { |
| typeHelper = new TypeHelper(this); |
| } |
| return typeHelper; |
| } |
| |
| protected IJpaType loadArrayType(String typeName) { |
| |
| IJpaType type = types.get(typeName); |
| |
| if (type == null) { |
| try { |
| // Try to see if the type is a JDK class, otherwise, just use the type name |
| // since IJpaType doesn't support array types |
| String javaTypeName = convertToJavaArrayType(typeName); |
| type = buildType(Class.forName(javaTypeName)); |
| } |
| catch (Exception e) { |
| |
| int index = typeName.indexOf("[]"); |
| int dimensionality = (typeName.length() - index) / 2; |
| |
| type = getType(typeName.substring(0, index)); |
| |
| type.setTypeDeclaration( |
| new JpaTypeDeclaration(type, new ITypeDeclaration[0], dimensionality) |
| ); |
| } |
| |
| types.put(typeName, type); |
| } |
| |
| return type; |
| } |
| |
| protected IJpaType loadInnerType(String typeName) { |
| |
| while (true) { |
| |
| int index = typeName.lastIndexOf("."); |
| |
| if (index == -1) { |
| return null; |
| } |
| |
| typeName = buildInnerTypeName(typeName, index); |
| IJpaType cachedType = types.get(typeName); |
| |
| if (cachedType != null) { |
| return cachedType; |
| } |
| |
| // Attempt to load the Java type |
| IType type = findType(typeName); |
| |
| // A Java type exists, return it |
| if (type != null) { |
| return buildType(type); |
| } |
| } |
| } |
| |
| protected IJpaType loadJavaArrayType(String typeName) { |
| |
| IJpaType type = types.get(typeName); |
| |
| if (type == null) { |
| try { |
| // Try to see if the type is a JDK class, otherwise, just use the type name |
| // since IJpaType doesn't support array types |
| type = buildType(Class.forName(typeName)); |
| } |
| catch (Exception e) { |
| |
| int index = typeName.lastIndexOf("["); |
| int dimensionality = index / 2; |
| |
| type = getType(typeName.substring(index, typeName.endsWith(";") ? typeName.length() - 1 : typeName.length())); |
| |
| type.setTypeDeclaration( |
| new JpaTypeDeclaration(type, new ITypeDeclaration[0], dimensionality) |
| ); |
| } |
| |
| types.put(typeName, type); |
| } |
| |
| return type; |
| } |
| |
| protected IJpaType loadTypeImp(String typeName) { |
| |
| IJpaType type = types.get(typeName); |
| |
| // The type was already cached, simply return it |
| if (type != null) { |
| return type; |
| } |
| |
| // First check for primitive, they don't have a corresponding IJpaType |
| Class<?> primitive = findPrimitive(typeName); |
| |
| if (primitive != null) { |
| return buildType(primitive); |
| } |
| |
| // Attempt to load the Java type |
| IType iType = findType(typeName); |
| |
| // A Java type exists, return it |
| if (iType != null) { |
| return buildType(iType); |
| } |
| |
| // Now try with a possible inner enum type |
| type = loadInnerType(typeName); |
| |
| // No Java type exists, create a "null" IJpaType |
| if (type == null) { |
| type = buildType(typeName); |
| } |
| |
| return type; |
| } |
| |
| protected IJpaType unresolvableType() { |
| if (unresolvableType == null) { |
| unresolvableType = new SimpleType(this, IJpaType.UNRESOLVABLE_TYPE); |
| } |
| return unresolvableType; |
| } |
| } |