blob: 36df38a61b67150cba2006009ca5c56ce695118d [file] [log] [blame]
/*
* Copyright (c) 1998, 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 from Oracle TopLink
package org.eclipse.persistence.jaxb.javamodel.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
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.Collection;
import java.util.List;
import org.eclipse.persistence.exceptions.JAXBException;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.jaxb.javamodel.JavaAnnotation;
import org.eclipse.persistence.jaxb.javamodel.JavaClass;
import org.eclipse.persistence.jaxb.javamodel.JavaClassInstanceOf;
import org.eclipse.persistence.jaxb.javamodel.JavaConstructor;
import org.eclipse.persistence.jaxb.javamodel.JavaField;
import org.eclipse.persistence.jaxb.javamodel.JavaMethod;
import org.eclipse.persistence.jaxb.javamodel.JavaPackage;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
/**
* INTERNAL:
* <p><b>Purpose:</b>A wrapper class for a JDK Class. This implementation
* of the EclipseLink JAXB 2.X Java model simply makes reflective calls on the
* underlying JDK object.
*
* <p><b>Responsibilities:</b>
* <ul>
* <li>Provide access to the underlying JDK Class' name, package,
* method/field names and parameters, annotations, etc.</li>
* </ul>
*
* @since Oracle TopLink 11.1.1.0.0
* @see org.eclipse.persistence.jaxb.javamodel.JavaClass
* @see java.lang.Class
*/
public class JavaClassImpl implements JavaClass {
protected ParameterizedType jType;
protected Class jClass;
protected JavaModelImpl javaModelImpl;
protected boolean isMetadataComplete;
protected JavaClass superClassOverride;
protected static final String XML_REGISTRY_CLASS_NAME = "jakarta.xml.bind.annotation.XmlRegistry";
public JavaClassImpl(Class javaClass, JavaModelImpl javaModelImpl) {
this.jClass = javaClass;
this.javaModelImpl = javaModelImpl;
isMetadataComplete = false;
}
public JavaClassImpl(ParameterizedType javaType, Class javaClass, JavaModelImpl javaModelImpl) {
this.jType = javaType;
this.jClass = javaClass;
this.javaModelImpl = javaModelImpl;
isMetadataComplete = false;
}
public void setJavaModelImpl(JavaModelImpl javaModel) {
this.javaModelImpl = javaModel;
}
@Override
public Collection<JavaClass> getActualTypeArguments() {
ArrayList<JavaClass> argCollection = new ArrayList<>();
if (jType != null) {
Type[] params = jType.getActualTypeArguments();
for (Type type : params) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
argCollection.add(new JavaClassImpl(pt, (Class) pt.getRawType(), javaModelImpl));
} else if(type instanceof WildcardType){
Type[] upperTypes = ((WildcardType)type).getUpperBounds();
if(upperTypes.length >0){
Type upperType = upperTypes[0];
if(upperType instanceof Class){
argCollection.add(javaModelImpl.getClass((Class) upperType));
}
}
} else if (type instanceof Class) {
argCollection.add(javaModelImpl.getClass((Class) type));
} else if(type instanceof GenericArrayType) {
Class genericTypeClass = (Class)((GenericArrayType)type).getGenericComponentType();
genericTypeClass = java.lang.reflect.Array.newInstance(genericTypeClass, 0).getClass();
argCollection.add(javaModelImpl.getClass(genericTypeClass));
} else if(type instanceof TypeVariable) {
Type[] boundTypes = ((TypeVariable) type).getBounds();
if(boundTypes.length > 0) {
Type boundType = boundTypes[0];
if(boundType instanceof Class) {
argCollection.add(javaModelImpl.getClass((Class) boundType));
}
}
}
}
}
return argCollection;
}
@Override
public String toString() {
return getName();
}
/**
* Assumes JavaType is a JavaClassImpl instance
*/
@Override
public JavaAnnotation getAnnotation(JavaClass arg0) {
// the only annotation we will return if isMetadataComplete == true is XmlRegistry
if (arg0 != null && (!isMetadataComplete || arg0.getQualifiedName().equals(XML_REGISTRY_CLASS_NAME))) {
Class annotationClass = ((JavaClassImpl) arg0).getJavaClass();
if (javaModelImpl.getAnnotationHelper().isAnnotationPresent(getAnnotatedElement(), annotationClass)) {
return new JavaAnnotationImpl(this.javaModelImpl.getAnnotationHelper().getAnnotation(getAnnotatedElement(), annotationClass));
}
}
return null;
}
@Override
public Collection<JavaAnnotation> getAnnotations() {
List<JavaAnnotation> annotationCollection = new ArrayList<>();
if (!isMetadataComplete) {
Annotation[] annotations = javaModelImpl.getAnnotationHelper().getAnnotations(getAnnotatedElement());
for (Annotation annotation : annotations) {
annotationCollection.add(new JavaAnnotationImpl(annotation));
}
}
return annotationCollection;
}
@Override
public Collection<JavaClass> getDeclaredClasses() {
List<JavaClass> classCollection = new ArrayList<>();
Class[] classes = jClass.getDeclaredClasses();
for (Class javaClass : classes) {
classCollection.add(javaModelImpl.getClass(javaClass));
}
return classCollection;
}
@Override
public JavaField getDeclaredField(String arg0) {
try {
return getJavaField(jClass.getDeclaredField(arg0));
} catch (NoSuchFieldException nsfe) {
return null;
}
}
@Override
public Collection<JavaField> getDeclaredFields() {
List<JavaField> fieldCollection = new ArrayList<>();
Field[] fields = PrivilegedAccessHelper.getDeclaredFields(jClass);
for (Field field : fields) {
if (!field.trySetAccessible()) {
AbstractSessionLog.getLog().log(SessionLog.FINE, SessionLog.MISC, "set_accessible_in",
"field", field.getName(), jClass.getName());
}
fieldCollection.add(getJavaField(field));
}
return fieldCollection;
}
/**
* Assumes JavaType[] contains JavaClassImpl instances
*/
@Override
public JavaMethod getDeclaredMethod(String arg0, JavaClass[] arg1) {
if (arg1 == null) {
arg1 = new JavaClass[0];
}
Class[] params = new Class[arg1.length];
for (int i=0; i<arg1.length; i++) {
JavaClass jType = arg1[i];
if (jType != null) {
params[i] = ((JavaClassImpl) jType).getJavaClass();
}
}
try {
return getJavaMethod(jClass.getDeclaredMethod(arg0, params));
} catch (NoSuchMethodException nsme) {
return null;
}
}
@Override
public Collection<JavaMethod> getDeclaredMethods() {
ArrayList<JavaMethod> methodCollection = new ArrayList<>();
Method[] methods = jClass.getDeclaredMethods();
for (Method method : methods) {
methodCollection.add(getJavaMethod(method));
}
return methodCollection;
}
@Override
public JavaConstructor getConstructor(JavaClass[] paramTypes) {
if (paramTypes == null) {
paramTypes = new JavaClass[0];
}
Class[] params = new Class[paramTypes.length];
for (int i=0; i<paramTypes.length; i++) {
JavaClass jType = paramTypes[i];
if (jType != null) {
params[i] = ((JavaClassImpl) jType).getJavaClass();
}
}
try {
Constructor constructor = PrivilegedAccessHelper.getConstructorFor(jClass, params, true);
return new JavaConstructorImpl(constructor, javaModelImpl);
} catch (NoSuchMethodException nsme) {
return null;
}
}
@Override
public JavaConstructor getDeclaredConstructor(JavaClass[] paramTypes) {
if (paramTypes == null) {
paramTypes = new JavaClass[0];
}
Class[] params = new Class[paramTypes.length];
for (int i=0; i<paramTypes.length; i++) {
JavaClass jType = paramTypes[i];
if (jType != null) {
params[i] = ((JavaClassImpl) jType).getJavaClass();
}
}
try {
return new JavaConstructorImpl(PrivilegedAccessHelper.getDeclaredConstructorFor(this.jClass, params, true), javaModelImpl);
} catch (NoSuchMethodException nsme) {
return null;
}
}
@Override
public Collection<JavaConstructor> getConstructors() {
Constructor[] constructors = this.jClass.getConstructors();
ArrayList<JavaConstructor> constructorCollection = new ArrayList(constructors.length);
for(Constructor next:constructors) {
constructorCollection.add(new JavaConstructorImpl(next, javaModelImpl));
}
return constructorCollection;
}
@Override
public Collection<JavaConstructor> getDeclaredConstructors() {
Constructor[] constructors = this.jClass.getDeclaredConstructors();
ArrayList<JavaConstructor> constructorCollection = new ArrayList(constructors.length);
for(Constructor next:constructors) {
constructorCollection.add(new JavaConstructorImpl(next, javaModelImpl));
}
return constructorCollection;
}
public JavaField getField(String arg0) {
try {
Field field = PrivilegedAccessHelper.getField(jClass, arg0, true);
return getJavaField(field);
} catch (NoSuchFieldException nsfe) {
return null;
}
}
public Collection getFields() {
ArrayList<JavaField> fieldCollection = new ArrayList<>();
Field[] fields = PrivilegedAccessHelper.getFields(jClass);
for (Field field : fields) {
fieldCollection.add(getJavaField(field));
}
return fieldCollection;
}
public Class getJavaClass() {
return jClass;
}
/**
* Assumes JavaType[] contains JavaClassImpl instances
*/
@Override
public JavaMethod getMethod(String arg0, JavaClass[] arg1) {
if (arg1 == null) {
arg1 = new JavaClass[0];
}
Class[] params = new Class[arg1.length];
for (int i=0; i<arg1.length; i++) {
JavaClass jType = arg1[i];
if (jType != null) {
params[i] = ((JavaClassImpl) jType).getJavaClass();
}
}
try {
Method method = PrivilegedAccessHelper.getMethod(jClass, arg0, params, true);
return getJavaMethod(method);
} catch (NoSuchMethodException nsme) {
return null;
}
}
@Override
public Collection<JavaMethod> getMethods() {
ArrayList<JavaMethod> methodCollection = new ArrayList<>();
Method[] methods = PrivilegedAccessHelper.getMethods(jClass);
for (Method method : methods) {
methodCollection.add(getJavaMethod(method));
}
return methodCollection;
}
@Override
public String getName() {
return jClass.getName();
}
@Override
public JavaPackage getPackage() {
return new JavaPackageImpl(jClass.getPackage(), javaModelImpl, isMetadataComplete);
}
@Override
public String getPackageName() {
if(jClass.getPackage() != null){
return jClass.getPackage().getName();
}else{
Class nonInnerClass = jClass;
Class enclosingClass = jClass.getEnclosingClass();
while(enclosingClass != null){
nonInnerClass = enclosingClass;
enclosingClass = nonInnerClass.getEnclosingClass();
}
String className = nonInnerClass.getCanonicalName();
if(className !=null){
int index = className.lastIndexOf('.');
if(index > -1){
return className.substring(0, index);
}
}
}
return null;
}
@Override
public String getQualifiedName() {
return jClass.getName();
}
@Override
public String getRawName() {
return jClass.getCanonicalName();
}
@Override
public JavaClass getSuperclass() {
if(this.superClassOverride != null) {
return this.superClassOverride;
}
if(jClass.isInterface()) {
Class[] superInterfaces = jClass.getInterfaces();
if(superInterfaces != null) {
if(superInterfaces.length == 1) {
return javaModelImpl.getClass(superInterfaces[0]);
} else {
Class parent = null;
for(Class next:superInterfaces) {
if(!(next.getName().startsWith("java.")
|| next.getName().startsWith("javax.")
|| next.getName().startsWith("jakarta."))) {
if(parent == null) {
parent = next;
} else {
throw JAXBException.invalidInterface(jClass.getName());
}
}
}
return javaModelImpl.getClass(parent);
}
}
}
return javaModelImpl.getClass(jClass.getSuperclass());
}
@Override
public Type[] getGenericInterfaces() {
return jClass.getGenericInterfaces();
}
@Override
public Type getGenericSuperclass() {
return jClass.getGenericSuperclass();
}
@Override
public boolean hasActualTypeArguments() {
return getActualTypeArguments().size() > 0;
}
public JavaField getJavaField(Field field) {
return new JavaFieldImpl(field, javaModelImpl, isMetadataComplete);
}
public JavaMethod getJavaMethod(Method method) {
return new JavaMethodImpl(method, javaModelImpl, isMetadataComplete);
}
public JavaClass getOwningClass() {
return javaModelImpl.getClass(jClass.getEnclosingClass());
}
@Override
public boolean isAnnotation() {
return jClass.isAnnotation();
}
@Override
public boolean isArray() {
return jClass.isArray();
}
public AnnotatedElement getAnnotatedElement() {
return jClass;
}
@Override
public boolean isAssignableFrom(JavaClass arg0) {
if (!(arg0 instanceof JavaClassImpl)) {
return false;
}
if(hasCustomSuperClass(arg0)) {
return this.customIsAssignableFrom(arg0);
}
return jClass.isAssignableFrom(((JavaClassImpl) arg0).getJavaClass());
}
private boolean customIsAssignableFrom(JavaClass arg0) {
JavaClassImpl jClass = (JavaClassImpl)arg0;
Class cls = jClass.getJavaClass();
if(cls == this.jClass) {
return true;
}
Class[] interfaces = cls.getInterfaces();
for(Class nextInterface:interfaces) {
if(nextInterface == this.jClass) {
return true;
}
if(customIsAssignableFrom(javaModelImpl.getClass(nextInterface))) {
return true;
}
}
if(!(jClass.isInterface())) {
JavaClassImpl superJavaClass = (JavaClassImpl)jClass.getSuperclass();
if(superJavaClass.getName().equals("java.lang.Object")) {
return this.jClass == superJavaClass.getJavaClass();
}
return customIsAssignableFrom(superJavaClass);
}
return false;
}
private boolean hasCustomSuperClass(JavaClass arg0) {
if(arg0 == null) {
return false;
}
if(!this.javaModelImpl.hasXmlBindings()) {
return false;
}
if(!(arg0.getClass() == this.getClass())) {
return false;
}
if(arg0.getName().equals("java.lang.Object")) {
return false;
}
JavaClassImpl jClass = (JavaClassImpl)arg0;
return jClass.getSuperClassOverride() != null || hasCustomSuperClass(jClass.getSuperclass());
}
@Override
public boolean isEnum() {
return jClass.isEnum();
}
@Override
public boolean isInterface() {
return jClass.isInterface();
}
@Override
public boolean isMemberClass() {
return jClass.isMemberClass();
}
@Override
public boolean isPrimitive() {
return jClass.isPrimitive();
}
@Override
public boolean isAbstract() {
return Modifier.isAbstract(getModifiers());
}
@Override
public boolean isPrivate() {
return Modifier.isPrivate(getModifiers());
}
@Override
public boolean isProtected() {
return Modifier.isProtected(getModifiers());
}
@Override
public boolean isPublic() {
return Modifier.isPublic(getModifiers());
}
@Override
public boolean isStatic() {
return Modifier.isStatic(getModifiers());
}
@Override
public int getModifiers() {
return jClass.getModifiers();
}
@Override
public boolean isFinal() {
return Modifier.isFinal(getModifiers());
}
@Override
public boolean isSynthetic() {
return jClass.isSynthetic();
}
@Override
public JavaClassInstanceOf instanceOf() {
return JavaClassInstanceOf.JAVA_CLASS_IMPL;
}
@Override
public JavaClass getComponentType() {
if(!isArray()) {
return null;
}
return javaModelImpl.getClass(this.jClass.getComponentType());
}
public JavaClass getSuperClassOverride() {
return superClassOverride;
}
public void setSuperClassOverride(JavaClass superClassOverride) {
this.superClassOverride = superClassOverride;
}
/**
* Set the indicator for XML metadata complete - if true,
* annotations will be ignored.
*
*/
void setIsMetadataComplete(Boolean isMetadataComplete) {
if(isMetadataComplete != null){
this.isMetadataComplete = isMetadataComplete;
}
}
@Override
public JavaAnnotation getDeclaredAnnotation(JavaClass arg0) {
// the only annotation we will return if isMetadataComplete == true is XmlRegistry
if (arg0 != null && (!isMetadataComplete || arg0.getQualifiedName().equals(XML_REGISTRY_CLASS_NAME))) {
Class annotationClass = ((JavaClassImpl) arg0).getJavaClass();
Annotation[] annotations = javaModelImpl.getAnnotationHelper().getDeclaredAnnotations(getAnnotatedElement());
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(annotationClass)) {
return new JavaAnnotationImpl(annotation);
}
}
}
return null;
}
@Override
public Collection getDeclaredAnnotations() {
List<JavaAnnotation> annotationCollection = new ArrayList<>();
if (!isMetadataComplete) {
Annotation[] annotations = javaModelImpl.getAnnotationHelper().getDeclaredAnnotations(getAnnotatedElement());
for (Annotation annotation : annotations) {
annotationCollection.add(new JavaAnnotationImpl(annotation));
}
}
return annotationCollection;
}
}