ObjectProperty.java
package space.sunqian.fs.object.schema;
import space.sunqian.annotation.Immutable;
import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;
import space.sunqian.fs.invoke.Invocable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
/**
* This interface represents the property info of {@link ObjectSchema}. It is very similar to the simple property of
* <a href="https://www.oracle.com/java/technologies/javase/javabeans-spec.html">JavaBeans</a>.
* <p>
* Two {@link ObjectProperty}s are considered equal if, and only if both their names and both their owners are equal.
*
* @author sunqian
*/
@Immutable
public interface ObjectProperty extends ObjectPropertyBase {
/**
* Returns the owner {@link ObjectSchema} of this property.
*
* @return the owner {@link ObjectSchema} of this property
*/
@Nonnull
ObjectSchema owner();
/**
* Returns whether this property is readable.
*
* @return whether this property is readable
*/
default boolean isReadable() {
return getter() != null;
}
/**
* Returns whether this property is writable.
*
* @return whether this property is writable
*/
default boolean isWritable() {
return setter() != null;
}
/**
* Returns the annotations on the backing field of this property. If this property doesn't have a backing field, an
* empty list will be returned.
*
* @return the annotations on the backing field of this property
*/
@Nonnull
@Immutable
List<@Nonnull Annotation> fieldAnnotations();
/**
* Returns the annotations on the getter method of this property. If this property doesn't have a getter method, an
* empty list will be returned.
*
* @return the annotations on the getter method of this property
*/
@Nonnull
@Immutable
List<@Nonnull Annotation> getterAnnotations();
/**
* Returns the annotations on the setter method of this property. If this property doesn't have a setter method, an
* empty list will be returned.
*
* @return the annotations on the setter method of this property
*/
@Nonnull
@Immutable
List<@Nonnull Annotation> setterAnnotations();
/**
* Returns the property value of the specified instance.
*
* @param inst the specified instance
* @return the property value of the specified instance
* @throws DataSchemaException if this property is not readable
*/
default @Nullable Object getValue(@Nonnull Object inst) throws DataSchemaException {
Invocable getter = getter();
if (getter == null) {
throw new DataSchemaException("The property is not readable: " + name() + ".");
}
return getter.invoke(inst);
}
/**
* Sets the property value of the specified instance.
*
* @param inst the specified instance
* @param value the property value
* @throws DataSchemaException if this property is not writable
*/
default void setValue(@Nonnull Object inst, @Nullable Object value) throws DataSchemaException {
Invocable setter = setter();
if (setter == null) {
throw new DataSchemaException("The property is not writable: " + name() + ".");
}
setter.invoke(inst, value);
}
/**
* Finds and returns the annotation of the specified type on getter method, setter method or backing field of this
* property, the searching order is that order. If the annotation is not found, {@code null} will be returned.
*
* @param annotationType the specified annotation type
* @return the annotation of the specified type on this property, or {@code null} if not found
*/
default <T extends Annotation> @Nullable T getAnnotation(@Nonnull Class<T> annotationType) {
Method getterMethod = getterMethod();
if (getterMethod != null) {
T annotation = getterMethod.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
}
Method setterMethod = setterMethod();
if (setterMethod != null) {
T annotation = setterMethod.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
}
Field field = field();
if (field != null) {
return field.getAnnotation(annotationType);
}
// for (Annotation annotation : getterAnnotations()) {
// if (Objects.equals(annotation.annotationType(), annotationType)) {
// return Fs.as(annotation);
// }
// }
// for (Annotation annotation : setterAnnotations()) {
// if (Objects.equals(annotation.annotationType(), annotationType)) {
// return Fs.as(annotation);
// }
// }
// for (Annotation annotation : fieldAnnotations()) {
// if (Objects.equals(annotation.annotationType(), annotationType)) {
// return Fs.as(annotation);
// }
// }
return null;
}
/**
* Returns whether this {@link ObjectProperty} is equal to the other {@link ObjectProperty}. They are considered
* equal if, and only if both their names and both their owners are equal.
*
* @param other the other {@link ObjectProperty}
* @return whether this {@link ObjectProperty} is equal to the other {@link ObjectProperty}
*/
boolean equals(Object other);
/**
* Returns the hash code of this {@link ObjectProperty}. The hash code is generated via {@link #name()} and
* {@link #owner()} like following codes:
* <pre>{@code
* int result = 1;
* result = 31 * result + name().hashCode();
* result = 31 * result + owner().hashCode();
* return result;
* }</pre>
*
* @return the hash code of this {@link ObjectProperty}
*/
int hashCode();
/**
* Returns a string representation of this {@link ObjectProperty}. The string is generated like following codes:
* <pre>{@code
* return name() + ": " + type().getTypeName();
* }</pre>
*
* @return a string representation of this {@link ObjectProperty}
*/
@Nonnull
String toString();
}