/*
* Copyright (C) 2011 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.errai.codegen.meta.impl.java;
import static org.jboss.errai.codegen.meta.MetaClassFactory.parameterizedAs;
import static org.jboss.errai.codegen.meta.MetaClassFactory.typeParametersOf;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.enterprise.util.TypeLiteral;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassFactory;
import org.jboss.errai.codegen.meta.MetaConstructor;
import org.jboss.errai.codegen.meta.MetaField;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.meta.MetaTypeVariable;
import org.jboss.errai.codegen.meta.impl.AbstractMetaClass;
import org.jboss.errai.codegen.util.GenUtil;
public class JavaReflectionClass extends AbstractMetaClass<Class> {
private Annotation[] _annotationsCache;
protected JavaReflectionClass(final Class clazz, final boolean erased) {
this(clazz, null, erased);
}
private JavaReflectionClass(final Class clazz, final Type type, final boolean erased) {
super(clazz);
_asClassCache = clazz;
if (!erased) {
if (type instanceof ParameterizedType) {
super.parameterizedType = new JavaReflectionParameterizedType((ParameterizedType) type);
}
if (clazz.getGenericSuperclass() instanceof ParameterizedType) {
super.genericSuperClass = new JavaReflectionParameterizedType((ParameterizedType) clazz.getGenericSuperclass());
}
}
}
private JavaReflectionClass(final TypeLiteral typeLiteral) {
super(typeLiteral.getRawType());
if (typeLiteral.getType() instanceof ParameterizedType) {
super.parameterizedType = new JavaReflectionParameterizedType((ParameterizedType) typeLiteral.getType());
}
}
public static MetaClass newInstance(final Class type) {
if (type == null) return null;
if (!MetaClassFactory.isCached(type.getName())) {
final MetaClass clazz = newUncachedInstance(type);
MetaClassFactory.getMetaClassCache().pushCache(clazz);
return clazz;
}
else {
return MetaClassFactory.get(type);
}
}
public static MetaClass newUncachedInstance(final Class type) {
return new JavaReflectionClass(type, false);
}
public static MetaClass newUncachedInstance(final Class clazz, final boolean erased) {
return new JavaReflectionClass(clazz, erased);
}
public static MetaClass newUncachedInstance(final Class clazz, final Type type) {
return new JavaReflectionClass(clazz, type, false);
}
public static MetaClass newInstance(final TypeLiteral type) {
return MetaClassFactory.get(type);
}
public static MetaClass newUncachedInstance(final TypeLiteral type) {
return new JavaReflectionClass(type);
}
@Override
public MetaClass getErased() {
if (parameterizedType == null) {
return this;
}
return new JavaReflectionClass(getEnclosedMetaObject(), true);
}
@Override
public String getName() {
return getEnclosedMetaObject().getSimpleName();
}
@Override
public String getFullyQualifiedName() {
return getEnclosedMetaObject().getName();
}
@Override
public String getCanonicalName() {
return getEnclosedMetaObject().getCanonicalName();
}
private String _packageName = null;
@Override
public String getPackageName() {
if (_packageName != null) {
return _packageName;
}
final Package pack = getEnclosedMetaObject().getPackage();
if (pack != null) {
_packageName = pack.getName();
}
return _packageName;
}
private MetaMethod[] fromMethodArray(final Method[] methods) {
final List<MetaMethod> methodList = new ArrayList<>();
for (final Method m : methods) {
// hack to exclude jacoco instrumented methods.
if (!m.isBridge() && !m.getName().startsWith("$jacoco")) {
methodList.add(new JavaReflectionMethod(this, m));
}
}
return methodList.toArray(new MetaMethod[methodList.size()]);
}
private MetaMethod[] _methodCache = null;
@Override
public MetaMethod[] getMethods() {
if (_methodCache != null) {
return _methodCache;
}
final Set<MetaMethod> meths = new LinkedHashSet<>();
Class<?> type = getEnclosedMetaObject();
if (type == null) {
return null;
}
final Set<String> processedMethods = new HashSet<>();
do {
for (final Method method : type.getDeclaredMethods()) {
final JavaReflectionMethod metaMethod = new JavaReflectionMethod(this, method);
final String readableMethodDecl = GenUtil.getMethodString(metaMethod);
if (!metaMethod.isPrivate() && !method.isBridge() && !processedMethods.contains(readableMethodDecl)) {
meths.add(metaMethod);
processedMethods.add(readableMethodDecl);
}
}
// we don't need to recurse on interfaces in this case because we already get the list of
// all inherited methods from Class.getMethods() -- interface methods are public
for (final Class<?> interfaceType : type.getInterfaces()) {
for (final MetaMethod ifaceMethod : Arrays.asList(JavaReflectionClass.newInstance(interfaceType).getMethods())) {
final String readableMethodDecl = GenUtil.getMethodString(ifaceMethod);
if (!processedMethods.contains(readableMethodDecl)) {
meths.add(ifaceMethod);
processedMethods.add(readableMethodDecl);
}
}
}
}
while ((type = type.getSuperclass()) != null);
_methodCache = meths.toArray(new MetaMethod[meths.size()]);
return _methodCache;
}
private transient volatile MetaMethod[] _declaredMethodCache;
@Override
public MetaMethod[] getDeclaredMethods() {
return _declaredMethodCache != null ? _declaredMethodCache
: (_declaredMethodCache = fromMethodArray(getEnclosedMetaObject().getDeclaredMethods()));
}
private static MetaField[] fromFieldArray(final Field[] methods) {
return Arrays.stream(methods).map(f -> new JavaReflectionField(f)).toArray(s -> new MetaField[s]);
}
private transient volatile MetaField[] _fieldCache;
@Override
public MetaField[] getFields() {
return _fieldCache != null ? _fieldCache : (_fieldCache = fromFieldArray(getEnclosedMetaObject().getFields()));
}
private transient volatile MetaField[] _declaredFieldCache;
@Override
public MetaField[] getDeclaredFields() {
return _declaredFieldCache != null ? _declaredFieldCache
: (_declaredFieldCache = fromFieldArray(getEnclosedMetaObject().getDeclaredFields()));
}
@Override
public MetaField getField(final String name) {
try {
final MetaField mFld;
if ("length".equals(name) && getEnclosedMetaObject().isArray()) {
mFld = new MetaField.ArrayLengthMetaField(this);
}
else {
mFld = new JavaReflectionField(getEnclosedMetaObject().getField(name));
}
return mFld;
}
catch (final Exception e) {
return null;
}
}
@Override
public MetaField getDeclaredField(final String name) {
try {
final MetaField mFld;
if ("length".equals(name) && getEnclosedMetaObject().isArray()) {
mFld = new MetaField.ArrayLengthMetaField(this);
}
else {
mFld = new JavaReflectionField(getEnclosedMetaObject().getDeclaredField(name));
}
return mFld;
}
catch (final Exception e) {
return null;
}
}
private transient volatile MetaConstructor[] constructorCache;
@Override
public MetaConstructor[] getConstructors() {
if (constructorCache != null) {
return constructorCache;
}
return constructorCache = Arrays.stream(getEnclosedMetaObject().getConstructors())
.map(c -> new JavaReflectionConstructor(c)).toArray(s -> new MetaConstructor[s]);
}
private transient volatile MetaConstructor[] declConstructorCache;
@Override
public MetaConstructor[] getDeclaredConstructors() {
if (getEnclosedMetaObject().isEnum()) {
// Enum constructors have strange metadata, so we avoid trouble by saying enums have no constructors
// (getParameterTypes().length != getGenericParameterTypes().length)
return new MetaConstructor[0];
}
if (declConstructorCache != null) {
return declConstructorCache;
}
return declConstructorCache = Arrays.stream(getEnclosedMetaObject().getDeclaredConstructors())
.map(c -> new JavaReflectionConstructor(c)).toArray(s -> new MetaConstructor[s]);
}
@Override
public MetaConstructor getConstructor(final Class... parameters) {
try {
return new JavaReflectionConstructor(getEnclosedMetaObject().getConstructor(parameters));
}
catch (final Exception e) {
return null;
}
}
@Override
public MetaConstructor getDeclaredConstructor(final Class... parameters) {
try {
return new JavaReflectionConstructor(getEnclosedMetaObject().getDeclaredConstructor(parameters));
}
catch (final Exception e) {
return null;
}
}
@Override
public MetaClass[] getDeclaredClasses() {
final Class[] declaredClasses = getEnclosedMetaObject().getDeclaredClasses();
final MetaClass[] declaredClassesMC = new MetaClass[declaredClasses.length];
int i = 0;
for (final Class c : declaredClasses) {
declaredClassesMC[i++] = MetaClassFactory.get(c);
}
return declaredClassesMC;
}
private MetaClass[] _interfacesCache;
@Override
public MetaClass[] getInterfaces() {
if (_interfacesCache != null) {
return _interfacesCache;
}
final List<MetaClass> metaClassList = new ArrayList<>();
final Type[] genIface = getEnclosedMetaObject().getGenericInterfaces();
int i = 0;
for (final Class<?> type : getEnclosedMetaObject().getInterfaces()) {
if (genIface != null) {
metaClassList.add(new JavaReflectionClass(type, genIface[i], false));
}
else {
metaClassList.add(new JavaReflectionClass(type, false));
}
i++;
}
return _interfacesCache = metaClassList.toArray(new MetaClass[metaClassList.size()]);
}
private MetaClass _superClass;
@Override
public MetaClass getSuperClass() {
if (_superClass != null)
return _superClass;
if (getGenericSuperClass() != null) {
_superClass = parameterizedAs(getEnclosedMetaObject().getSuperclass(), typeParametersOf(getGenericSuperClass()
.getTypeParameters()));
}
else {
_superClass = newInstance(getEnclosedMetaObject().getSuperclass());
}
return _superClass;
}
@Override
public MetaClass getComponentType() {
return MetaClassFactory.get(getEnclosedMetaObject().getComponentType());
}
@Override
public synchronized Annotation[] getAnnotations() {
if (_annotationsCache == null) {
_annotationsCache = getEnclosedMetaObject().getAnnotations();
}
return _annotationsCache;
}
@Override
public MetaTypeVariable[] getTypeParameters() {
return JavaReflectionUtil.fromTypeVariable(getEnclosedMetaObject().getTypeParameters());
}
@Override
public boolean isPrimitive() {
return getEnclosedMetaObject().isPrimitive();
}
@Override
public boolean isVoid() {
return getEnclosedMetaObject().equals(Void.TYPE);
}
@Override
public boolean isInterface() {
return getEnclosedMetaObject().isInterface();
}
@Override
public boolean isAbstract() {
return (getEnclosedMetaObject().getModifiers() & Modifier.ABSTRACT) != 0;
}
@Override
public boolean isArray() {
return getEnclosedMetaObject().isArray();
}
@Override
public boolean isEnum() {
return getEnclosedMetaObject().isEnum();
}
@Override
public boolean isAnnotation() {
return getEnclosedMetaObject().isAnnotation();
}
@Override
public boolean isPublic() {
return (getEnclosedMetaObject().getModifiers() & Modifier.PUBLIC) != 0;
}
@Override
public boolean isPrivate() {
return (getEnclosedMetaObject().getModifiers() & Modifier.PRIVATE) != 0;
}
@Override
public boolean isProtected() {
return (getEnclosedMetaObject().getModifiers() & Modifier.PROTECTED) != 0;
}
@Override
public boolean isFinal() {
return (getEnclosedMetaObject().getModifiers() & Modifier.FINAL) != 0;
}
@Override
public boolean isStatic() {
return (getEnclosedMetaObject().getModifiers() & Modifier.STATIC) != 0;
}
@Override
public boolean isSynthetic() {
return getEnclosedMetaObject().isSynthetic();
}
@Override
public boolean isAnonymousClass() {
return getEnclosedMetaObject().isAnonymousClass();
}
private final Map<Integer, MetaClass> _arrayTypeCache = new HashMap<>();
@Override
public MetaClass asArrayOf(final int dimensions) {
MetaClass arrayType = _arrayTypeCache.get(dimensions);
if (arrayType == null) {
_arrayTypeCache.put(dimensions, arrayType = MetaClassFactory.getArrayOf(getEnclosedMetaObject(), dimensions));
}
return arrayType;
}
}