package org.javers.common.reflection;
import org.javers.common.collections.Sets;
import org.javers.common.validation.Validate;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Optional;
import java.util.Set;
import static java.util.Collections.unmodifiableSet;
/**
* Enhanced Field or Method, deals with Java type erasure.
* <br/><br/>
*
* A Member (getter or field) inherited from a Generic superclass with actual (return) type resolved.
* <br/><br/>
*
* Formal type parameter of superclass is resolved to actual type argument of subclass.
*
* @author bartosz walacik
*/
public abstract class JaversMember<T extends Member> {
private final T rawMember; //delegate
private final Optional<Type> resolvedReturnType;
/**
* @param resolvedReturnType nullable
*/
public JaversMember(T rawMember, Type resolvedReturnType) {
Validate.argumentIsNotNull(rawMember);
this.rawMember = rawMember;
this.resolvedReturnType = Optional.ofNullable(resolvedReturnType);
setAccessibleIfNecessary(rawMember);
}
protected abstract Type getRawGenericType();
public abstract Class<?> getRawType();
public T getRawMember() {
return rawMember;
}
public Type getGenericResolvedType(){
if (resolvedReturnType.isPresent()){
return resolvedReturnType.get();
}
return getRawGenericType();
}
public Class<?> getDeclaringClass(){
return rawMember.getDeclaringClass();
}
public String name(){
return rawMember.getName();
}
public String propertyName(){
return rawMember.getName();
}
public Set<Annotation> getAnnotations() {
return unmodifiableSet(Sets.asSet(((AccessibleObject) rawMember).getAnnotations()));
}
public Set<Class<? extends Annotation>> getAnnotationTypes() {
return Sets.transform(getAnnotations(), ann -> ann.annotationType());
}
public boolean hasAnnotation(Set<String> aliases) {
if (aliases == null) {
return false;
}
return getAnnotationTypes().stream().anyMatch(annType -> aliases.contains(annType.getSimpleName()));
}
public abstract Object getEvenIfPrivate(Object target);
public abstract void setEvenIfPrivate(Object target, Object value);
void setAccessibleIfNecessary(Member rawMember) {
if(!isPublic(rawMember))
{
((AccessibleObject)rawMember).setAccessible(true); //that's Java Reflection API ...
}
}
private boolean isPublic(Member member){
return Modifier.isPublic(member.getModifiers());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JaversMember that = (JaversMember) o;
return this.rawMember.equals(that.rawMember) && this.resolvedReturnType.equals(that.resolvedReturnType);
}
@Override
public int hashCode() {
return rawMember.hashCode() + resolvedReturnType.hashCode();
}
}