io.github.mmm.entity.property.link.LinkProperty Maven / Gradle / Ivy
/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0 */
package io.github.mmm.entity.property.link;
import java.util.function.Function;
import java.util.function.Supplier;
import io.github.mmm.bean.WritableBean;
import io.github.mmm.entity.Entity;
import io.github.mmm.entity.bean.EntityBean;
import io.github.mmm.entity.id.Id;
import io.github.mmm.entity.id.IdMarshalling;
import io.github.mmm.entity.link.AbstractLink;
import io.github.mmm.entity.link.IdLink;
import io.github.mmm.entity.link.Link;
import io.github.mmm.entity.link.LinkMapper;
import io.github.mmm.entity.property.id.IdProperty;
import io.github.mmm.marshall.StructuredReader;
import io.github.mmm.marshall.StructuredWriter;
import io.github.mmm.property.PropertyMetadata;
import io.github.mmm.property.ReadableProperty;
import io.github.mmm.property.criteria.CriteriaPredicate;
import io.github.mmm.property.criteria.PredicateOperator;
import io.github.mmm.property.object.ObjectProperty;
import io.github.mmm.value.PropertyPath;
import io.github.mmm.value.ReadableValue;
import io.github.mmm.value.converter.TypeMapper;
/**
* {@link ObjectProperty} with {@link Link} {@link #getValue() value} {@link Link#getTarget() pointing to} an
* {@link io.github.mmm.entity.bean.EntityBean entity}.
*
* @param the generic type of the {@link Link#getTarget() linked} {@link io.github.mmm.entity.bean.EntityBean
* entity}.
*
* @since 1.0.0
*/
public class LinkProperty extends ObjectProperty> {
private Class entityClass;
private Function, E> resolver;
private LinkMapper typeMapper;
/**
* The constructor.
*
* @param name the {@link #getName() name}.
* @param entityClass the optional {@link Class} of the linked entity.
* @param metadata the {@link #getMetadata() metadata}.
*/
public LinkProperty(String name, Class entityClass, PropertyMetadata> metadata) {
this(name, entityClass, metadata, null);
}
/**
* The constructor.
*
* @param name the {@link #getName() name}.
* @param entityClass the optional {@link Class} of the linked entity.
* @param metadata the {@link #getMetadata() metadata}.
* @param resolver the optional {@link IdLink#of(Id, Function) resolver function}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public LinkProperty(String name, Class entityClass, PropertyMetadata> metadata,
Function, E> resolver) {
super(name, (Class) Link.class, metadata);
this.entityClass = entityClass;
this.resolver = resolver;
}
/**
* The constructor.
*
* @param name the {@link #getName() name}.
* @param value the (initial) {@link #get() value}.
* @param metadata the {@link #getMetadata() metadata}.
*/
public LinkProperty(String name, Link value, PropertyMetadata> metadata) {
this(name, value, metadata, null);
}
/**
* The constructor.
*
* @param name the {@link #getName() name}.
* @param value the (initial) {@link #get() value}.
* @param metadata the {@link #getMetadata() metadata}.
* @param resolver the optional {@link IdLink#of(Id, Function) resolver function}.
*/
public LinkProperty(String name, Link value, PropertyMetadata> metadata, Function, E> resolver) {
super(name, value, metadata);
this.entityClass = value.getId().getEntityClass();
this.resolver = resolver;
}
@Override
protected void doSet(Link newValue) {
if (newValue != null) {
Id id = newValue.getId();
if (id != null) {
if (this.entityClass == null) {
this.entityClass = id.getEntityClass();
} else {
Class idEntityType = id.getEntityClass();
if (idEntityType == null) {
if (newValue instanceof IdLink) {
newValue = ((IdLink) newValue).withType(this.entityClass);
}
} else if (!this.entityClass.isAssignableFrom(idEntityType)) {
throw new IllegalArgumentException("Cannot set link of type " + idEntityType.getName() + " to property "
+ getName() + " with incompatible entity type " + this.entityClass.getName());
}
}
}
}
super.doSet(newValue);
}
/**
* @return the linked {@link EntityBean entity}.
* @see Link#getTarget()
*/
public E getEntity() {
Link link = get();
if (link == null) {
return null;
}
return link.getTarget();
}
/**
* @param entity the new value of {@link #getEntity()}.
*/
public void setEntity(E entity) {
Link link = Link.of(entity);
set(link);
}
/**
* @return the {@link Id#getEntityClass() entity class}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Class getEntityClass() {
if (this.entityClass == null) {
Link link = get();
if (link != null) {
Id id = link.getId();
if (id != null) {
this.entityClass = id.getEntityClass();
} else if (link.isResolved()) {
E target = link.getTarget();
this.entityClass = (Class) target.getType().getJavaClass();
}
}
}
return this.entityClass;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public TypeMapper, Id> getTypeMapper() {
if (this.typeMapper == null) {
this.typeMapper = new LinkMapper(this.resolver);
}
return (TypeMapper) this.typeMapper;
}
@Override
public boolean isValueMutable() {
return true; // IdLink without resolver function is actually immutable but lets keep it simple
}
@Override
protected Supplier extends Link> createReadOnlyExpression() {
final ReadOnlyLink readOnlyLink = new ReadOnlyLink();
return () -> {
Link link = get();
if (link == null) {
return null;
}
return readOnlyLink;
};
}
@Override
protected Link readValue(StructuredReader reader, boolean apply) {
Id id = IdMarshalling.get().readObject(reader, this.entityClass);
IdLink link = IdLink.of(id, this.resolver);
if (apply) {
setValue(link);
}
return link;
}
@Override
public void writeValue(StructuredWriter writer, Link link) {
Id id = null;
if (link != null) {
id = link.getId();
}
IdMarshalling.get().writeObject(writer, id);
}
/**
* @see #eq(Object)
* @param other the literal {@link Id} to compare with using {@link PredicateOperator#EQ = (equal)}.
* @return the resulting {@link CriteriaPredicate}.
*/
public CriteriaPredicate eq(Id other) {
return eq(Link.of(other));
}
/**
* @see #eq(Object)
* @param other the {@link IdProperty} to compare with using {@link PredicateOperator#EQ = (equal)}.
* @return the resulting {@link CriteriaPredicate}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public CriteriaPredicate eq(IdProperty other) {
return predicate((ReadableProperty) this, PredicateOperator.EQ, (PropertyPath) other);
}
/**
* @see #eq(Object)
* @param other the literal {@link Entity} whose {@link Entity#getId() ID} to compare with using
* {@link PredicateOperator#EQ = (equal)}.
* @return the resulting {@link CriteriaPredicate}.
*/
public CriteriaPredicate eq(E other) {
if ((other != null) && (other.getId() == null)) {
return eq(other.Id());
}
return eq(Link.of(other));
}
/**
* @see #neq(Object)
* @param other the literal {@link Id} to compare with using {@link PredicateOperator#NEQ != (not-equal)}.
* @return the resulting {@link CriteriaPredicate}.
*/
public CriteriaPredicate neq(Id other) {
return neq(Link.of(other));
}
/**
* @see #neq(Object)
* @param other the literal {@link Entity} whose {@link Entity#getId() ID} to compare with using
* {@link PredicateOperator#NEQ != (not-equal)}.
* @return the resulting {@link CriteriaPredicate}.
*/
public CriteriaPredicate neq(E other) {
if ((other != null) && (other.getId() == null)) {
return neq(other.Id());
}
return neq(Link.of(other));
}
/**
* @see #neq(Object)
* @param other the {@link IdProperty} to compare with using {@link PredicateOperator#NEQ != (not-equal)}.
* @return the resulting {@link CriteriaPredicate}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public CriteriaPredicate neq(IdProperty other) {
return predicate((ReadableProperty) this, PredicateOperator.NEQ, (PropertyPath) other);
}
private CriteriaPredicate predicate(ReadableProperty property1, PredicateOperator op,
PropertyPath property2) {
return CriteriaPredicate.of(property1, op, property2);
}
/**
* ATTENTION:
* This is an internal method for framework code.
*
* @param resolver new resolver {@link Function}.
* @see IdLink#setResolver(Function)
*/
@SuppressWarnings("unchecked")
public void setResolver(Function, E> resolver) {
// TODO: checks?
this.resolver = resolver;
this.typeMapper = null;
Link link = doGet();
if ((link != null) && !link.isResolved()) {
if (link instanceof IdLink idLink) {
idLink.setResolver(resolver);
}
}
}
@Override
public void copyValue(ReadableValue> other) {
Link link = other.get();
if (link != null) {
link = Link.of(link.getId());
}
set(link);
}
private class ReadOnlyLink extends AbstractLink {
@Override
public Id getId() {
return get().getId();
}
@Override
public E getTarget() {
E target = get().getTarget();
if (target != null) {
target = WritableBean.getReadOnly(target);
}
return target;
}
@Override
public boolean isResolved() {
return get().isResolved();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy