package io.micronaut.core.beans;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArgumentUtils;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Objects;
@UsedByGeneratedCode
@Internal
public abstract class AbstractBeanProperty<B, P> implements BeanProperty<B, P> {
private final BeanIntrospection<B> introspection;
private final Class<B> beanType;
private final Class<P> type;
private final String name;
private final AnnotationMetadata annotationMetadata;
private final Argument[] typeArguments;
@Internal
@UsedByGeneratedCode
protected AbstractBeanProperty(
@NonNull BeanIntrospection<B> introspection,
@NonNull Class<P> type,
@NonNull String name,
@Nullable AnnotationMetadata annotationMetadata,
@Nullable Argument[] typeArguments) {
this.introspection = introspection;
this.type = type;
this.beanType = introspection.getBeanType();
this.name = name;
this.annotationMetadata = annotationMetadata == null ? AnnotationMetadata.EMPTY_METADATA : annotationMetadata;
this.typeArguments = typeArguments;
}
@Override public @NonNull String getName() {
return name;
}
@NonNull
@Override
public Class<P> getType() {
return type;
}
@Override
public Argument<P> asArgument() {
if (typeArguments != null) {
return Argument.of(type, name, getAnnotationMetadata(), typeArguments);
} else {
return Argument.of(type, name, getAnnotationMetadata());
}
}
@NonNull
@Override
public BeanIntrospection<B> getDeclaringBean() {
return introspection;
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
@Nullable
@Override
public final P get(@NonNull B bean) {
ArgumentUtils.requireNonNull("bean", bean);
if (!beanType.isInstance(bean)) {
throw new IllegalArgumentException("Invalid bean [" + bean + "] for type: " + introspection.getBeanType());
}
if (isWriteOnly()) {
throw new UnsupportedOperationException("Cannot read from a write-only property");
}
return readInternal(bean);
}
@Override
public final void set(@NonNull B bean, @Nullable P value) {
ArgumentUtils.requireNonNull("bean", bean);
if (!beanType.isInstance(bean)) {
throw new IllegalArgumentException("Invalid bean [" + bean + "] for type: " + introspection.getBeanType());
}
if (isReadOnly()) {
throw new UnsupportedOperationException("Cannot write a read-only property: " + getName());
}
if (value != null && !ReflectionUtils.getWrapperType(getType()).isInstance(value)) {
throw new IllegalArgumentException("Specified value [" + value + "] is not of the correct type: " + getType());
}
writeInternal(bean, value);
}
@SuppressWarnings("WeakerAccess")
@UsedByGeneratedCode
@Internal
protected abstract void writeInternal(@NonNull B bean, @Nullable P value);
@SuppressWarnings("WeakerAccess")
@UsedByGeneratedCode
@Internal
protected abstract P readInternal(@NonNull B bean);
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractBeanProperty<?, ?> that = (AbstractBeanProperty<?, ?>) o;
return Objects.equals(beanType, that.beanType) &&
Objects.equals(type, that.type) &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(beanType, type, name);
}
@Override
public String toString() {
return "BeanProperty{" +
"beanType=" + beanType +
", type=" + type +
", name='" + name + '\'' +
'}';
}
}