All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cp.elements.beans.model.Property Maven / Gradle / Ivy

/*
 * Copyright 2011-Present Author or Authors.
 *
 * 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.cp.elements.beans.model;

import static org.cp.elements.lang.ElementsExceptionsFactory.newPropertyReadException;
import static org.cp.elements.lang.ElementsExceptionsFactory.newPropertyWriteException;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;

import org.cp.elements.beans.PropertyReadException;
import org.cp.elements.beans.PropertyWriteException;
import org.cp.elements.beans.annotation.Required;
import org.cp.elements.beans.model.support.AbstractIndexedProperty;
import org.cp.elements.lang.Assert;
import org.cp.elements.lang.ClassUtils;
import org.cp.elements.lang.Describable;
import org.cp.elements.lang.Nameable;
import org.cp.elements.lang.ObjectUtils;
import org.cp.elements.lang.PrimitiveTypeUtils;
import org.cp.elements.lang.annotation.Dsl;
import org.cp.elements.lang.annotation.FluentApi;
import org.cp.elements.lang.annotation.NotNull;
import org.cp.elements.lang.annotation.NullSafe;
import org.cp.elements.lang.annotation.Nullable;
import org.cp.elements.lang.annotation.Transient;
import org.cp.elements.lang.reflect.MethodNotFoundException;
import org.cp.elements.lang.reflect.ModifierUtils;
import org.cp.elements.util.CollectionUtils;

/**
 * Abstract Data Type (ADT) modeling a {@link Object bean} property.
 *
 * @author John Blum
 * @see java.beans.PropertyDescriptor
 * @see java.lang.Comparable
 * @see java.lang.annotation.Annotation
 * @see java.lang.reflect.AnnotatedElement
 * @see java.lang.reflect.Field
 * @see java.lang.reflect.Method
 * @see org.cp.elements.lang.Describable
 * @see org.cp.elements.lang.Nameable
 * @see org.cp.elements.lang.annotation.FluentApi
 * @since 1.0.0
 */
@FluentApi
@SuppressWarnings("unused")
public class Property implements Comparable, Describable, Nameable {

  public static final Function> ALL_ANNOTATIONS_RESOLVER =
    annotatedElement -> annotatedElement != null
      ? CollectionUtils.asSet(annotatedElement.getAnnotations())
      : Collections.emptySet();

  public static final Function> DECLARED_ANNOTATIONS_RESOLVER =
    annotatedElement -> annotatedElement != null
      ? CollectionUtils.asSet(annotatedElement.getDeclaredAnnotations())
      : Collections.emptySet();

  protected static final Function> DEFAULT_ANNOTATIONS_RESOLVER =
    DECLARED_ANNOTATIONS_RESOLVER;

  protected static final Predicate IS_REQUIRED = annotatedElement ->
    annotatedElement != null && annotatedElement.isAnnotationPresent(Required.class);

  protected static final Predicate IS_TRANSIENT_ANNOTATED = annotatedElement ->
    annotatedElement != null
      && (annotatedElement.isAnnotationPresent(Transient.class)
        || annotatedElement.isAnnotationPresent(java.beans.Transient.class));

  protected static final Predicate IS_TRANSIENT_FIELD = annotatedElement ->
    annotatedElement instanceof Field && ModifierUtils.isTransient(annotatedElement);

  protected static final Predicate IS_TRANSIENT = IS_TRANSIENT_FIELD.or(IS_TRANSIENT_ANNOTATED);

  /**
   * Factory method used to construct a new {@link Property} from the given, required {@link BeanModel}
   * modeling the bean containing the property and a given, required {@link PropertyDescriptor}
   * describing the bean property.
   *
   * @param beanModel {@link BeanModel} modeling the bean containing the property.
   * @param propertyDescriptor {@link PropertyDescriptor} describing the bean property.
   * @return a new {@link Property} modeling the bean property.
   * @throws IllegalArgumentException if the {@link BeanModel} or {@link PropertyDescriptor} are {@literal null}.
   * @see #Property(BeanModel, PropertyDescriptor)
   * @see org.cp.elements.beans.model.BeanModel
   * @see org.cp.elements.lang.annotation.Dsl
   * @see java.beans.PropertyDescriptor
   */
  @Dsl
  public static @NotNull Property from(@NotNull BeanModel beanModel, @NotNull PropertyDescriptor propertyDescriptor) {
    return new Property(beanModel, propertyDescriptor);
  }

  /**
   * Resolves all {@link Annotation Annotations} declared on the given {@link AnnotatedElement} by using the given,
   * required {@link Function} encapsulating the {@literal Strategy} for resolving {@link Annotation Annotations}
   * declared on an {@link AnnotatedElement}.
   * 

* The default {@literal Strategies} provided by this {@link Property} class includes * {@link AnnotatedElement#getAnnotations()} and {@link AnnotatedElement#getDeclaredAnnotations()}. * * @param {@link Class type} of {@link AnnotatedElement}, such as {@link Field} or {@link Method}. * @param annotationsResolver {@link Function} encapsulating the {@literal Strategy} used to resolve * the {@link Annotation Annotations} declared on the {@link AnnotatedElement}; should not be {@literal null}, * but defaults to {@link #DECLARED_ANNOTATIONS_RESOLVER}. * @param annotatedElementAnnotationsCache {@link Map} acting as a {@literal cache} to lookup previously resolved * {@link Annotation Annotations} declared on the {@link AnnotatedElement}; must not be {@literal null}. * @param annotatedElement {@link AnnotatedElement}, such as a {@link Field} or {@link Method}, from which * the declared {@link Annotation Annotations} are resolved. * @return a {@link Set} of {@link Annotation Annotations} resolved from the {@link AnnotatedElement}. * @see java.lang.reflect.AnnotatedElement * @see java.lang.annotation.Annotation * @see java.util.function.Function * @see java.util.Map */ @NullSafe protected static Set getAnnotatedElementAnnotations( @NotNull Function> annotationsResolver, @NotNull Map> annotatedElementAnnotationsCache, @Nullable T annotatedElement) { return annotatedElement != null ? nullSafeAnnotatedElementAnnotationsCache(annotatedElementAnnotationsCache) .computeIfAbsent(annotatedElement, nullSafeAnnotationsResolver(annotationsResolver)) : Collections.emptySet(); } @NullSafe private static @NotNull Map> nullSafeAnnotatedElementAnnotationsCache( @Nullable Map> annotatedElementAnnotations) { return annotatedElementAnnotations != null ? annotatedElementAnnotations : new AbstractMap<>() { // Key is never cached; Value is always computed with the given Function. @Override public Set computeIfAbsent(T key, @NotNull Function> mappingFunction) { return mappingFunction.apply(key); } @Override public Set>> entrySet() { return Collections.emptySet(); } }; } @NullSafe private static @NotNull Function> nullSafeAnnotationsResolver( @Nullable Function> annotationsResolver) { return annotationsResolver != null ? annotationsResolver : DEFAULT_ANNOTATIONS_RESOLVER; } private final transient AtomicReference fieldReference = new AtomicReference<>(null); private final transient FieldResolver fieldResolver = new PropertyNameFieldResolver(); private final BeanModel beanModel; private final transient Map> fieldAnnotations = new WeakHashMap<>(); private final transient Map> methodAnnotations = new WeakHashMap<>(); private final PropertyDescriptor propertyDescriptor; /** * Constructs a new {@link Property} initialized with the given, required {@link BeanModel} * modeling the bean containing this {@link Property} along with the given, required {@link PropertyDescriptor} * describing the bean {@link Property}. * * @param beanModel {@link BeanModel} modeling the bean containing the property; must not be {@literal null}. * @param propertyDescriptor {@link PropertyDescriptor} describing the bean property; must not be {@literal null}. * @throws IllegalArgumentException if the {@link BeanModel} or {@link PropertyDescriptor} are {@literal null}. * @see org.cp.elements.beans.model.BeanModel * @see java.beans.PropertyDescriptor */ protected Property(@NotNull BeanModel beanModel, @NotNull PropertyDescriptor propertyDescriptor) { this.beanModel = ObjectUtils.requireObject(beanModel, "BeanModel is required"); this.propertyDescriptor = ObjectUtils.requireObject(propertyDescriptor, "PropertyDescriptor is required"); } /** * Gets all resolvable {@link Annotation Annotations} declared on this {@link Property}. *

* A {@link Property Property's} {@link Annotation Annotations} consist of * all {@link Annotation Annotation's} declared on the {@link Property Property's} {@link Field} * if the {@link Property} is not {@link #isDerived()}, as well as all {@link Annotation Annotations} * declared on the {@link #getReadMethod() accessor method} and {@link #getWriteMethod() mutator method} * of the {@link Property}, providing the {@link Property} is both {@link #isReadable()} and {@link #isWritable()}. * * @return a {@link Set} of all resolvable {@link Annotation Annotations} declared on this {@link Property}. * @see java.lang.annotation.Annotation * @see #getAnnotations(Function) * @see java.util.Set */ public Set getAnnotations() { return getAnnotations(DEFAULT_ANNOTATIONS_RESOLVER); } /** * Gets all resolvable {@link Annotation Annotations} declared on this {@link Property} using the given, required * {@link Function} encapsulating the {@literal Strategy} for resolving {@link Annotation Annotations} * on all {@link AnnotatedElement AnnotatedElements} of this {@link Property}. *

* A {@link Property Property's} {@link Annotation Annotations} consist of * all {@link Annotation Annotation's} declared on the {@link Property Property's} {@link Field} * if the {@link Property} is not {@link #isDerived()}, as well as all {@link Annotation Annotations} * declared on the {@link #getReadMethod() accessor method} and {@link #getWriteMethod() mutator method} * of the {@link Property}, providing the {@link Property} is both {@link #isReadable()} and {@link #isWritable()}. * * @param annotationsResolver {@link Function} encapsulating the {@literal Strategy} used to resolve * the {@link Annotation Annotations} declared on an {@link AnnotatedElement}; should not be {@literal null}, * but defaults to {@link #DECLARED_ANNOTATIONS_RESOLVER}. * @return a {@link Set} of all resolved {@link Annotation Annotations} declared on this {@link Property}. * @see #getWriteMethodAnnotations(Function) * @see #getReadMethodAnnotations(Function) * @see #getFieldAnnotations(Function) * @see java.lang.annotation.Annotation * @see java.util.function.Function * @see java.util.Set */ public Set getAnnotations(@NotNull Function> annotationsResolver) { Set propertyAnnotations = new HashSet<>(); propertyAnnotations.addAll(getFieldAnnotations(annotationsResolver)); propertyAnnotations.addAll(getReadMethodAnnotations(annotationsResolver)); propertyAnnotations.addAll(getWriteMethodAnnotations(annotationsResolver)); return propertyAnnotations; } /** * Gets an {@link Annotation} declared on this {@link Property} of the given {@link Class type}. * * @param {@link Class type} of the {@link Annotation}. * @param annotationType {@link Class type} of {@link Annotation} to find. * @return an {@link Annotation} declared on this {@link Property} of the given {@link Class type} * or {@literal null} if an {@link Annotation} of the given {@link Class type} was not declared * on this {@link Property}. * @see #getAnnotation(Class, Function) * @see java.lang.annotation.Annotation */ public @Nullable T getAnnotation(@NotNull Class annotationType) { return getAnnotation(annotationType, DEFAULT_ANNOTATIONS_RESOLVER); } /** * Gets an {@link Annotation} declared on this {@link Property} of the given {@link Class type}. * * @param {@link Class type} of the {@link Annotation}. * @param annotationType {@link Class type} of {@link Annotation} to find. * @param annotationsResolver {@link Function} encapsulating the {@literal Strategy} used to resolve * the {@link Annotation Annotations} declared on an {@link AnnotatedElement}; should not be {@literal null}, * but defaults to {@link #DECLARED_ANNOTATIONS_RESOLVER}. * @return an {@link Annotation} declared on this {@link Property} of the given {@link Class type} * or {@literal null} if an {@link Annotation} of the given {@link Class type} was not declared * on this {@link Property}. * @see java.lang.annotation.Annotation * @see java.util.function.Function * @see #getAnnotations(Function) */ public @Nullable T getAnnotation(@NotNull Class annotationType, @NotNull Function> annotationsResolver) { return getAnnotations(annotationsResolver).stream() .filter(annotation -> annotation.annotationType().equals(annotationType)) .findFirst() .map(annotationType::cast) .orElse(null); } /** * Gets the {@link BeanAdapter bean} to which this {@link Property} belongs. * * @return the {@link BeanAdapter bean} to which this {@link Property} belongs. * @see org.cp.elements.beans.model.BeanModel#getBean() * @see org.cp.elements.beans.model.BeanAdapter * @see org.cp.elements.lang.annotation.Dsl * @see #getBeanModel() */ @Dsl protected @NotNull BeanAdapter getBean() { return getBeanModel().getBean(); } /** * Gets the {@link BeanModel} modeling the bean containing this {@link Property}. * * @return the {@link BeanModel} modeling the bean containing this {@link Property}. * @see org.cp.elements.beans.model.BeanModel * @see org.cp.elements.lang.annotation.Dsl */ @Dsl public @NotNull BeanModel getBeanModel() { return this.beanModel; } /** * Gets the {@literal JavaBeans} {@link PropertyDescriptor} describing this bean {@link Property}. * * @return the {@literal JavaBeans} {@link PropertyDescriptor} describing this bean {@link Property}. * @see java.beans.PropertyDescriptor */ public @NotNull PropertyDescriptor getDescriptor() { return this.propertyDescriptor; } /** * Gets the {@link Object} {@link Field} backing this bean {@link Property} if not derived. *

* The {@link Object} {@link Field} may be {@literal null} if this bean {@link Property} is derived * from some other {@link Object} {@link Field}. Think of an age property on a Person type derived * from a person's date of birth. * * @return the {@link Object} {@link Field} backing this bean {@link Property} if not derived. * @see org.cp.elements.beans.model.FieldResolver#resolve(Property) * @see java.lang.reflect.Field */ protected @Nullable Field getField() { return this.fieldReference.updateAndGet(field -> field != null ? field : this.fieldResolver.resolve(this)); } /** * Gets all resolvable {@link Annotation Annotations} declared on the {@link Property Property's} * {@link #getField() Field}. * * @param annotationsResolver {@link Function} encapsulating the {@literal Strategy} used to resolve * the {@link Annotation Annotations} declared on the {@link Property Property's} {@link Field}; * should not be {@literal null}, but defaults to {@link #DECLARED_ANNOTATIONS_RESOLVER}. * @return a {@link Set} of {@link Annotation Annotations} resolved from * the {@link Property Property's} {@link Field}. Returns an {@link Collections#emptySet()} * if this {@link Property} is {@link #isDerived() derived}. * @see #getAnnotatedElementAnnotations(Function, Map, AnnotatedElement) * @see java.lang.annotation.Annotation * @see java.util.Set * @see #getField() */ protected Set getFieldAnnotations( @NotNull Function> annotationsResolver) { return getAnnotatedElementAnnotations(annotationsResolver, this.fieldAnnotations, getField()); } /** * Alias for {@link #getReadMethod()}. * * @return the {@link Method} used to read from this {@link Property}. * @see java.lang.reflect.Method * @see #getReadMethod() */ protected @Nullable Method getAccessorMethod() { return getReadMethod(); } /** * Gets the {@link Method} used to read from this {@link Property}. * * @return the {@link Method} used to read from this {@link Property}; * may return {@literal null} if the {@link Property} cannot be read. * @see java.beans.PropertyDescriptor#getReadMethod() * @see java.lang.reflect.Method * @see #getWriteMethod() * @see #getDescriptor() */ protected @Nullable Method getReadMethod() { Method readMethod = getDescriptor().getReadMethod(); try { return readMethod != null ? ClassUtils.getDeclaredMethod(getBeanModel().getTargetType(), readMethod) : null; } catch (MethodNotFoundException ignore) { return readMethod; } } /** * Gets all resolvable {@link Annotation Annotations} declared on the {@link Property Property's} * {@link #getReadMethod() accessor method}. * * @param annotationsResolver {@link Function} encapsulating the {@literal Strategy} used to resolve * the {@link Annotation Annotations} declared on this {@link Property Property's} * {@link #getReadMethod() accessor method}; should not be {@literal null}, * but defaults to {@link #DECLARED_ANNOTATIONS_RESOLVER}. * @return a {@link Set} of {@link Annotation Annotations} resolved from * the {@link Property Property's} {@link #getReadMethod() accessor method}. * Returns an {@link Collections#emptySet()} if this {@link Property} is not {@link #isReadable() readable}. * @see #getAnnotatedElementAnnotations(Function, Map, AnnotatedElement) * @see java.lang.annotation.Annotation * @see #getReadMethod() * @see java.util.Set */ protected Set getReadMethodAnnotations( @NotNull Function> annotationsResolver) { return getAnnotatedElementAnnotations(annotationsResolver, this.methodAnnotations, getReadMethod()); } /** * Gets the underlying {@link Object POJO} backing the bean for this {@link Property}. * * @return the underlying {@link Object POJO} backing the bean for this {@link Property}. * @see org.cp.elements.beans.model.BeanAdapter#getTarget() * @see java.lang.Object * @see #getBean() */ protected @NotNull Object getTargetObject() { return getBean().getTarget(); } /** * Alias for {@link #getWriteMethod()}. * * @return the {@link Method} used to write to this {@link Property}. * @see java.lang.reflect.Method * @see #getWriteMethod() */ protected @Nullable Method getMutatorMethod() { return getWriteMethod(); } /** * Gets the {@link Method} used to write to this {@link Property}. * * @return the {@link Method} used to write to this {@link Property}; * may return {@literal null} if the {@link Property} cannot be written, * such as for a read-only {@link Property}. * @see java.beans.PropertyDescriptor#getWriteMethod() * @see java.lang.reflect.Method * @see #getDescriptor() * @see #getReadMethod() */ protected @Nullable Method getWriteMethod() { Method writeMethod = getDescriptor().getWriteMethod(); try { return writeMethod != null ? ClassUtils.getDeclaredMethod(getBeanModel().getTargetType(), writeMethod) : null; } catch (MethodNotFoundException ignore) { return writeMethod; } } /** * Gets all resolvable {@link Annotation Annotations} declared on the {@link Property Property's} * {@link #getReadMethod() mutator method}. * * @param annotationsResolver {@link Function} encapsulating the {@literal Strategy} used to resolve * the {@link Annotation Annotations} declared on this {@link Property Property's} * {@link #getWriteMethod() mutator method}; should not be {@literal null}, * but defaults to {@link #DECLARED_ANNOTATIONS_RESOLVER}. * @return a {@link Set} of {@link Annotation Annotations} resolved from * the {@link Property Property's} {@link #getWriteMethod() mutator method}. * Returns an {@link Collections#emptySet()} if this {@link Property} is not {@link #isWritable() writable}. * @see #getAnnotatedElementAnnotations(Function, Map, AnnotatedElement) * @see java.lang.annotation.Annotation * @see #getWriteMethod() * @see java.util.Set */ protected Set getWriteMethodAnnotations( @NotNull Function> annotationsResolver) { return getAnnotatedElementAnnotations(annotationsResolver, this.methodAnnotations, getWriteMethod()); } /** * Determines whether this {@link Property} is annotated. * * @return a boolean value indicating whether this {@link Property} is annotated. * @see #getAnnotations() */ public boolean isAnnotated() { return !getAnnotations().isEmpty(); } /** * Determines whether this {@link Property} is annotated with an {@link Annotation} of the given, required * {@link Annotation#annotationType()}. * * @param annotationType {@link Class type} of the {@link Annotation} to evaluate. * @return a boolean value indicating whether this {@link Property} is annotated with an {@link Annotation} * of the given, required {@link Annotation#annotationType()}. * @see java.lang.annotation.Annotation#annotationType() * @see #getAnnotations() */ public boolean isAnnotatedWith(@NotNull Class annotationType) { return getAnnotations().stream() .map(Annotation::annotationType) .anyMatch(actualAnnotationType -> actualAnnotationType.equals(annotationType)); } /** * Determines whether the {@link Class type} of this {@link Property} is an {@link Class#isArray() array}. * * @return a boolean value indicating whether the {@link Class type} of this {@link Property} * is an {@link Class#isArray() array}. * @see #isCollectionLike() * @see #getType() */ public boolean isArrayTyped() { return ClassUtils.isArray(getType()); } /** * Determines whether the {@link Class type} of this {@link Property} is {@link Collection Collection-like}. * * @return a boolean value indicating whether the {@link Class type} of this {@link Property} * is {@link Collection Collection-like}. * @see #isTypedAs(Class) * @see #isArrayTyped() */ public boolean isCollectionLike() { return isArrayTyped() || isTypedAs(List.class) || isTypedAs(Map.class) || isTypedAs(Set.class); } /** * Determines whether this {@link Property} is derived from another bean {@link Property}. *

* This means this {@link Property} is not backed by an {@link Object} {@link Field}. * For example, this {@link Property} may represent the {@literal age} property of a {@literal Person} type * where age is computed from the person's {@literal date of birth} using the {@literal birthdate} {@link Field}. * * @return a boolean value indicating whether this {@link Property} is derived from * another bean {@link Property}. * @see #getField() */ public boolean isDerived() { return getField() == null; } /** * Determines whether this {@link Property} is an {@link AbstractIndexedProperty Indexed-based Property}. *

* A {@link Property} is an {@link AbstractIndexedProperty Indexed-based Property} * if the {@link #getType() Property's Type} is an indexed-based data structure, such as an array, * {@link List} or {@link Map}. * * @return a boolean value indicating whether this {@link Property} * is an {@link AbstractIndexedProperty Indexed-based Property}. * @see AbstractIndexedProperty#isIndexed(Property) * @see #isArrayTyped() */ public boolean isIndexed() { return isArrayTyped() || AbstractIndexedProperty.isIndexed(this); } /** * Determines whether the {@link #getType()} of this {@link Property} is assignment compatible with * the given {@link Class type}. *

* If the {@link Class type} argument is {@literal null}, then this method returns {@literal false}. * * @param type {@link Class} used to evaluate this {@link Property#getType() property type}. * @return a boolean value indicating whether the {@link #getType()} of this {@link Property} * is assignment compatible with the given {@link Class type}. * @see java.lang.Class * @see #getType() */ @NullSafe public boolean isTypedAs(@Nullable Class type) { return type != null && type.isAssignableFrom(getType()); } /** * Alias for {@link #isSerializable()}. * * @return the value of {@link #isSerializable()}. * @see #isSerializable() */ public boolean isPersistent() { return isSerializable(); } /** * Determine whether this {@link Property} can be read. * * @return a boolean value indicating whether this {@link Property} can be read. * @see #getReadMethod() * @see #isWritable() */ public boolean isReadable() { return getReadMethod() != null; } /** * Determines whether this {@link Property} is required. *

* A {@link Property} is {@literal required} if the {@link Field} backing this {@link Property}, * the {@link Method getter method} or the {@link Method setter method} are annotated with * the Element's {@link Required} annotation. * * @return a boolean value indicating whether this {@link Property} is required. */ public boolean isRequired() { return IS_REQUIRED.test(getField()) || IS_REQUIRED.test(getReadMethod()) || IS_REQUIRED.test(getWriteMethod()); } /** * Determines whether this {@link Property} is serializable. *

* A {@link Property} is {@literal serializable} if the {@link Property} is {@link #isReadable()} * and is not {@link #isTransient()}. * * @return a boolean value indicating whether this {@link Property} is serializable. * @see #isTransient() * @see #isReadable() */ public boolean isSerializable() { return isReadable() && !isTransient(); } /** * Determines whether this {@link Property} is transient. *

* A {@link Property} is {@literal transient} if the {@link Field} backing this {@link Property} is declared with * the {@literal transient} keyword, or the {@link Method getter method} or {@link Method setter method} * are annotated with the Elements {@link Transient} annotation or the JavaBeans {@link java.beans.Transient} * annotation. * * @return a boolean value indicating whether this {@link Property} is transient. * @see #isSerializable() */ public boolean isTransient() { return IS_TRANSIENT.test(getField()) || IS_TRANSIENT.test(getReadMethod()) || IS_TRANSIENT.test(getWriteMethod()); } /** * Determine whether this {@link Property} can be written. * * @return a boolean value indicating whether this {@link Property} can be written. * @see #getWriteMethod() * @see #isReadable() */ public boolean isWritable() { return getWriteMethod() != null; } /** * Gets the {@link String name} of this {@link Property}. * * @return the {@link String name} of this {@link Property}. * @see java.lang.String */ public @NotNull String getName() { return getDescriptor().getName(); } /** * Gets the {@link Class type} of this {@link Property}. * * @return the {@link Class type} of this {@link Property}. * @see java.lang.Class */ @SuppressWarnings("unchecked") public @NotNull Class getType() { return ObjectUtils.returnFirstNonNullValue(getDescriptor().getPropertyType(), Object.class); } /** * Gets {@link Object value} of this {@link Property} as a value of the given {@link #getType() Property's type}. * * @param {@link Class type} of this {@link Property}. * @return the {@link Object value} of this {@link Property} as a value of * the given {@link #getType() Property's type}. * @see #getValue() * @see #getType() */ @SuppressWarnings("unchecked") public @Nullable T getTypedValue() { return (T) PrimitiveTypeUtils.primitiveToWrapperType().apply(getType()).cast(getValue()); } /** * Gets the {@link Object value} of this {@link Property}. * * @return the {@link Object value} of this {@link Property}. * @throws PropertyReadException if this {@link Property} cannot be read. * @see java.lang.Object */ public @Nullable Object getValue() { Assert.state(isReadable(), newPropertyReadException("Property [%s] of bean [%s] is not readable", getName(), getBean())); return ObjectUtils.invoke(getTargetObject(), getReadMethod(), new Object[0], Object.class); } /** * Sets the {@link Object value} of this {@link Property}. * * @param value {@link Object value} to set for this {@link Property}. * @throws PropertyWriteException if this {@link Property} cannot be written. * @see java.lang.Object */ public void setValue(Object value) { Assert.state(isWritable(), newPropertyWriteException("Property [%s] of bean [%s] is not writable", getName(), getBean())); ObjectUtils.invoke(getTargetObject(), getWriteMethod(), new Object[] { value }, Void.class); } /** * Returns this {@link Property} as a typed {@link Property} of the given {@link Property} {@link Class type}. * * @param {@link Class type} of {@link Property}. * @param type {@link Class type} to cast this {@link Property} to; must not be {@literal null}. * @return a typed {@link Property} of the given {@link Property} {@link Class type}. * @throws org.cp.elements.lang.IllegalTypeException if this {@link Property} is not an instance of * the given {@link Class type}. */ public @NotNull T asTypedProperty(@NotNull Class type) { Assert.isInstanceOf(this, type); return type.cast(this); } /** * Compares this {@link Property} to the given, required {@link Property} to determine (sort) order. *

* {@link Property Properties} are ordered by {@link #getName()}. * * @param property {@link Property} to compare with this {@link Property}. * @return an {@link Integer} value indicating the order of this {@link Property} relative to the given, * required {@link Property}. Returns less than zero if this {@link Property} is sorted before the given * {@link Property}, greater than zero if this {@link Property} is sorted after the given {@link Property} * and zero if this {@link Property} is equal to the given {@link Property}. * @see #getName() */ @Override public int compareTo(@NotNull Property property) { return this.getName().compareTo(property.getName()); } /** * Determines whether this {@link Property} is equal to the given {@link Object}. * * @param obj {@link Object} to evaluate for equality with this {@link Property}. * @return a boolean value indicating whether this {@link Property} is equal to the given {@link Object}. * @see #getName() */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Property that)) { return false; } return ObjectUtils.equals(this.getName(), that.getName()); } /** * Computes the {@literal hash value} for this {{@link Property}. * * @return an {@link Integer} value with the hash code of this {@link Property}. * @see #getName() */ @Override public int hashCode() { return ObjectUtils.hashCodeOf(getName()); } /** * Returns the {@link String name} of this {@link Property}. * * @return the {@link String name} of this {@link Property}. * @see #getName() */ @Override public String toString() { return getName(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy