blob: e087d78734927af53142ab11ba16dda6a2ee4f46 [file] [log] [blame]
/*
* Copyright (c) 2006, 2021 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation
//
package org.eclipse.persistence.jpa.tests.jpql.tools.spi.java;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.persistence.jpa.jpql.tools.TypeHelper;
import org.eclipse.persistence.jpa.jpql.tools.spi.IConstructor;
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;
import org.eclipse.persistence.jpa.jpql.tools.utility.iterable.SnapshotCloneIterable;
/**
* The concrete implementation of {@link IType} that is wrapping a Java type.
*
* @version 2.5
* @since 2.3
* @author Pascal Filion
*/
public class JavaType implements IType {
/**
* The cached {@link IConstructor IConstructors}.
*/
private Collection<IConstructor> constructors;
/**
* The list of names for the {@link Enum}'s constants otherwise an empty array.
*/
private String[] enumConstants;
/**
* The actual Java type.
*/
private Class<?> type;
/**
* The cached {@link ITypeDeclaration} for this {@link IType}.
*/
private ITypeDeclaration typeDeclaration;
/**
* The fully qualified name of the Java type.
*/
private String typeName;
/**
* The external form of a type repository.
*/
private ITypeRepository typeRepository;
/**
* Creates a new <code>JavaType</code>.
*
* @param typeRepository The external form of a type repository
* @param type The actual Java type wrapped by this class
*/
public JavaType(ITypeRepository typeRepository, Class<?> type) {
this(typeRepository, type.getName());
this.type = type;
}
/**
* Creates a new <code>JavaType</code>.
*
* @param typeRepository The external form of a type repository
* @param typeName The fully qualified name of the Java type
*/
public JavaType(ITypeRepository typeRepository, String typeName) {
super();
this.typeName = typeName;
this.typeRepository = typeRepository;
}
protected IConstructor buildConstructor(Constructor<?> constructor) {
return new JavaConstructor(this, constructor);
}
protected Collection<IConstructor> buildConstructors() {
if (type == null) {
return Collections.emptyList();
}
Constructor<?>[] javaConstructors = type.getDeclaredConstructors();
Collection<IConstructor> constructors = new ArrayList<IConstructor>(javaConstructors.length);
for (Constructor<?> javaConstructor : javaConstructors) {
constructors.add(buildConstructor(javaConstructor));
}
return constructors;
}
protected String[] buildEnumConstants() {
if ((type == null) || !type.isEnum()) {
return ExpressionTools.EMPTY_STRING_ARRAY;
}
Object[] enumConstants = type.getEnumConstants();
String[] names = new String[enumConstants.length];
for (int index = enumConstants.length; --index >= 0; ) {
names[index] = ((Enum<?>) enumConstants[index]).name();
}
return names;
}
@Override
public Iterable<IConstructor> constructors() {
if (constructors == null) {
constructors = buildConstructors();
}
return new SnapshotCloneIterable<IConstructor>(constructors);
}
@Override
public boolean equals(IType type) {
return (this == type) || typeName.equals(type.getName());
}
@Override
public boolean equals(Object object) {
return equals((IType) object);
}
@Override
public String[] getEnumConstants() {
if (enumConstants == null) {
enumConstants = buildEnumConstants();
}
return enumConstants;
}
@Override
public String getName() {
return typeName;
}
/**
* Returns the encapsulated {@link Class}, which is the actual type.
*
* @return The actual Java type, if <code>null</code> is returned; then the class could not be resolved
*/
public Class<?> getType() {
return type;
}
@Override
public ITypeDeclaration getTypeDeclaration() {
if (typeDeclaration == null) {
typeDeclaration = new JavaTypeDeclaration(
typeRepository,
this,
null,
(type != null) ? type.isArray() : false
);
}
return typeDeclaration;
}
/**
* Returns the type repository for the application.
*
* @return The repository of {@link IType ITypes}
*/
protected ITypeRepository getTypeRepository() {
return typeRepository;
}
@Override
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
return (type != null) && type.isAnnotationPresent(annotationType);
}
@Override
public int hashCode() {
return typeName.hashCode();
}
@Override
public boolean isAssignableTo(IType otherType) {
if (equals(otherType)) {
return true;
}
if (!isResolvable() || !otherType.isResolvable()) {
return false;
}
// Make sure both types are not primitives since isAssignableFrom() does not work.
// For instance long and Long can't be compared but they are valid for JPQL query
TypeHelper typeHelper = typeRepository.getTypeHelper();
IType thisType = typeHelper.convertPrimitive(this);
otherType = typeHelper.convertPrimitive(otherType);
Class<?> thisClass = ((JavaType) thisType) .type;
Class<?> otherClass = ((JavaType) otherType).type;
return otherClass.isAssignableFrom(thisClass);
}
@Override
public boolean isEnum() {
return (type != null) && type.isEnum();
}
@Override
public boolean isResolvable() {
return (type != null);
}
@Override
public String toString() {
return typeName;
}
}