Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.evitadb.api.proxy.impl.entity.GetReferenceMethodClassifier Maven / Gradle / Ivy
/*
*
* _ _ ____ ____
* _____ _(_) |_ __ _| _ \| __ )
* / _ \ \ / / | __/ _` | | | | _ \
* | __/\ V /| | || (_| | |_| | |_) |
* \___| \_/ |_|\__\__,_|____/|____/
*
* Copyright (c) 2023
*
* Licensed under the Business Source License, Version 1.1 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/FgForrest/evitaDB/blob/master/LICENSE
*
* 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 io.evitadb.api.proxy.impl.entity;
import io.evitadb.api.exception.ContextMissingException;
import io.evitadb.api.exception.EntityClassInvalidException;
import io.evitadb.api.proxy.ProxyReferenceFactory;
import io.evitadb.api.proxy.SealedEntityProxy.ProxyType;
import io.evitadb.api.proxy.impl.ProxyUtils;
import io.evitadb.api.proxy.impl.ProxyUtils.OptionalProducingOperator;
import io.evitadb.api.proxy.impl.ProxyUtils.ResultWrapper;
import io.evitadb.api.proxy.impl.SealedEntityProxyState;
import io.evitadb.api.requestResponse.data.Droppable;
import io.evitadb.api.requestResponse.data.EntityContract;
import io.evitadb.api.requestResponse.data.EntityReferenceContract;
import io.evitadb.api.requestResponse.data.ReferenceContract;
import io.evitadb.api.requestResponse.data.SealedEntity;
import io.evitadb.api.requestResponse.data.annotation.CreateWhenMissing;
import io.evitadb.api.requestResponse.data.annotation.Entity;
import io.evitadb.api.requestResponse.data.annotation.EntityRef;
import io.evitadb.api.requestResponse.data.annotation.Reference;
import io.evitadb.api.requestResponse.data.annotation.ReferenceRef;
import io.evitadb.api.requestResponse.data.annotation.RemoveWhenExists;
import io.evitadb.api.requestResponse.data.structure.EntityReference;
import io.evitadb.api.requestResponse.data.structure.ReferenceDecorator;
import io.evitadb.api.requestResponse.schema.EntitySchemaContract;
import io.evitadb.api.requestResponse.schema.ReferenceSchemaContract;
import io.evitadb.dataType.EvitaDataTypes;
import io.evitadb.function.ExceptionRethrowingFunction;
import io.evitadb.function.TriFunction;
import io.evitadb.utils.Assert;
import io.evitadb.utils.ClassUtils;
import io.evitadb.utils.CollectorUtils;
import io.evitadb.utils.NamingConvention;
import io.evitadb.utils.NumberUtils;
import io.evitadb.utils.ReflectionLookup;
import one.edee.oss.proxycian.CurriedMethodContextInvocationHandler;
import one.edee.oss.proxycian.DirectMethodClassification;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import static io.evitadb.api.proxy.impl.ProxyUtils.getResolvedTypes;
import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;
/**
* Identifies methods that are used to get reference from a sealed entity and provides their implementation.
*
* @author Jan Novotný ([email protected] ), FG Forrest a.s. (c) 2023
*/
public class GetReferenceMethodClassifier extends DirectMethodClassification {
/**
* We may reuse singleton instance since advice is stateless.
*/
public static final GetReferenceMethodClassifier INSTANCE = new GetReferenceMethodClassifier();
/**
* Tries to identify parent from the class field related to the constructor parameter.
*
* @param expectedType class the constructor belongs to
* @param parameter constructor parameter
* @param reflectionLookup reflection lookup
* @return attribute name derived from the annotation if found
*/
@Nullable
public static ExceptionRethrowingFunction getExtractorIfPossible(
@Nonnull EntitySchemaContract schema,
@Nonnull Map referencedEntitySchemas,
@Nonnull Class expectedType,
@Nonnull Parameter parameter,
@Nonnull ReflectionLookup reflectionLookup,
@Nonnull BiFunction, EntityContract, Object> proxyFactory,
@Nonnull ProxyReferenceFactory proxyReferenceFactory
) {
// now we need to identify reference schema that is being requested
final ReferenceSchemaContract referenceSchema = getReferenceSchema(
expectedType, parameter, reflectionLookup, schema
);
// if not found, this method is not classified by this implementation
if (referenceSchema == null) {
return null;
} else {
// finally provide implementation that will retrieve the reference or reference entity from the entity
final String referenceName = referenceSchema.getName();
// now we need to identify the return type
@SuppressWarnings("rawtypes") final Class returnType = parameter.getType();
final Class>[] resolvedTypes = getResolvedTypes(parameter, expectedType);
@SuppressWarnings("rawtypes") final Class collectionType;
@SuppressWarnings("rawtypes") final Class itemType;
if (Collection.class.equals(resolvedTypes[0]) || List.class.isAssignableFrom(resolvedTypes[0]) || Set.class.isAssignableFrom(resolvedTypes[0])) {
collectionType = resolvedTypes[0];
itemType = resolvedTypes.length > 1 ? resolvedTypes[1] : EntityReference.class;
} else if (resolvedTypes[0].isArray()) {
collectionType = resolvedTypes[0];
itemType = returnType.getComponentType();
} else {
collectionType = null;
itemType = resolvedTypes[0];
}
// return the appropriate result
final Entity entityInstance = reflectionLookup.getClassAnnotation(itemType, Entity.class);
final EntityRef entityRefInstance = reflectionLookup.getClassAnnotation(itemType, EntityRef.class);
if (EntityReferenceContract.class.isAssignableFrom(itemType)) {
return getEntityReference(referenceName, collectionType);
} else if (Number.class.isAssignableFrom(EvitaDataTypes.toWrappedForm(itemType))) {
//noinspection unchecked
return getEntityId(referenceName, collectionType, itemType);
} else if (entityInstance != null) {
return getReferencedEntity(schema, referenceSchema, entityInstance.name(), collectionType, itemType, proxyFactory);
} else if (entityRefInstance != null) {
return getReferencedEntity(schema, referenceSchema, entityRefInstance.value(), collectionType, itemType, proxyFactory);
} else {
return getReference(referencedEntitySchemas, referenceName, collectionType, expectedType, itemType, proxyReferenceFactory);
}
}
}
/**
* Retrieves appropriate reference schema from the annotations on the method. If no Evita annotation is found
* it tries to match the attribute name by the name of the method.
*/
@Nullable
public static ReferenceSchemaContract getReferenceSchema(
@Nonnull Method method,
@Nonnull ReflectionLookup reflectionLookup,
@Nonnull EntitySchemaContract entitySchema
) {
final Reference referenceInstance = reflectionLookup.getAnnotationInstanceForProperty(method, Reference.class);
final ReferenceRef referenceRefInstance = reflectionLookup.getAnnotationInstanceForProperty(method, ReferenceRef.class);
if (referenceInstance != null) {
return entitySchema.getReferenceOrThrowException(
ofNullable(referenceInstance.name())
.filter(it -> !it.isBlank())
.orElseGet(() -> ReflectionLookup.getPropertyNameFromMethodName(method.getName()))
);
} else if (referenceRefInstance != null) {
return entitySchema.getReferenceOrThrowException(referenceRefInstance.value());
} else if (!reflectionLookup.hasAnnotationForPropertyInSamePackage(method, Reference.class) && ClassUtils.isAbstract(method)) {
final Optional referenceName = ReflectionLookup.getPropertyNameFromMethodNameIfPossible(method.getName());
return referenceName
.flatMap(it -> entitySchema.getReferenceByName(it, NamingConvention.CAMEL_CASE))
.orElse(null);
} else {
return null;
}
}
/**
* Retrieves appropriate reference schema from the annotations on the parameter. If no Evita annotation is found
* it tries to match the attribute name by the name of the parameter.
*/
@Nullable
private static ReferenceSchemaContract getReferenceSchema(
@Nonnull Class> expectedType,
@Nonnull Parameter parameter,
@Nonnull ReflectionLookup reflectionLookup,
@Nonnull EntitySchemaContract entitySchema
) {
final String parameterName = parameter.getName();
final Reference referenceInstance = reflectionLookup.getAnnotationInstanceForProperty(expectedType, parameterName, Reference.class);
final ReferenceRef referenceRefInstance = reflectionLookup.getAnnotationInstanceForProperty(expectedType, parameterName, ReferenceRef.class);
if (referenceInstance != null) {
return entitySchema.getReferenceOrThrowException(referenceInstance.name());
} else if (referenceRefInstance != null) {
return entitySchema.getReferenceOrThrowException(referenceRefInstance.value());
} else {
final Optional referenceName = ReflectionLookup.getPropertyNameFromMethodNameIfPossible(parameter.getName());
return referenceName
.flatMap(it -> entitySchema.getReferenceByName(it, NamingConvention.CAMEL_CASE))
.orElse(null);
}
}
/**
* Method wraps passed {@link ReferenceContract} into a custom proxy instance of particular type.
*/
@Nullable
private static T createProxy(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class itemType,
@Nonnull BiFunction, EntityContract, T> proxyFactory,
@Nonnull ReferenceContract reference,
@Nonnull Function> entityExtractor
) {
Assert.isTrue(
reference instanceof ReferenceDecorator,
() -> ContextMissingException.referencedEntityContextMissing(entityName, referenceName)
);
final ReferenceDecorator referenceDecorator = (ReferenceDecorator) reference;
return entityExtractor.apply(referenceDecorator)
.map(it -> proxyFactory.apply(itemType, it))
.orElse(null);
}
/**
* Creates an implementation of the method returning a single referenced entity wrapped into {@link EntityReference} object.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler singleEntityReferenceResult(
@Nonnull String referenceName,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> resultWrapper.wrap(
() -> {
final Stream references = referenceExtractor.apply(theState.entity(), referenceName);
if (references == null) {
return null;
} else {
final Optional firstReference = references.findFirst();
if (firstReference.isEmpty()) {
return null;
} else {
final ReferenceContract theReference = firstReference.get();
return new EntityReference(theReference.getReferencedEntityType(), theReference.getReferencedPrimaryKey());
}
}
}
);
}
/**
* Creates an implementation of the method returning a single referenced entity wrapped into {@link EntityReference} object.
*/
@Nonnull
private static ExceptionRethrowingFunction singleEntityReferenceResult(
@Nonnull String referenceName
) {
return sealedEntity -> {
final Optional firstReference = sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.findFirst();
if (firstReference.isEmpty()) {
return null;
} else {
final ReferenceContract theReference = firstReference.get();
return new EntityReference(theReference.getReferencedEntityType(), theReference.getReferencedPrimaryKey());
}
};
}
/**
* Creates an implementation of the method returning a list of referenced entities wrapped into {@link EntityReference} object.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler listOfEntityReferencesResult(
@Nonnull String referenceName,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(refs -> refs
.map(it -> new EntityReference(it.getReferencedEntityType(), it.getReferencedPrimaryKey()))
.toList()
).orElse(Collections.emptyList())
);
}
/**
* Creates an implementation of the method returning a list of referenced entities wrapped into {@link EntityReference} object.
*/
@Nonnull
private static ExceptionRethrowingFunction listOfEntityReferencesResult(
@Nonnull String referenceName
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(it -> new EntityReference(it.getReferencedEntityType(), it.getReferencedPrimaryKey()))
.toList();
} else {
return Collections.emptyList();
}
};
}
/**
* Creates an implementation of the method returning a set of referenced entities wrapped into {@link EntityReference} object.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler setOfEntityReferencesResult(
@Nonnull String referenceName,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(
refs -> refs
.map(it -> new EntityReference(it.getReferencedEntityType(), it.getReferencedPrimaryKey()))
.collect(CollectorUtils.toUnmodifiableLinkedHashSet())
).orElse(Collections.emptySet())
);
}
/**
* Creates an implementation of the method returning a set of referenced entities wrapped into {@link EntityReference} object.
*/
@Nonnull
private static ExceptionRethrowingFunction setOfEntityReferencesResult(
@Nonnull String referenceName
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(it -> new EntityReference(it.getReferencedEntityType(), it.getReferencedPrimaryKey()))
.collect(CollectorUtils.toUnmodifiableLinkedHashSet());
} else {
return Collections.emptySet();
}
};
}
/**
* Creates an implementation of the method returning an array of referenced entities wrapped into {@link EntityReference} object.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler arrayOfEntityReferencesResult(
@Nonnull String referenceName,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(refs -> refs
.map(it -> new EntityReference(it.getReferencedEntityType(), it.getReferencedPrimaryKey()))
.toArray(EntityReference[]::new)
).orElse(null)
);
}
/**
* Creates an implementation of the method returning an array of referenced entities wrapped into {@link EntityReference} object.
*/
@Nonnull
private static ExceptionRethrowingFunction arrayOfEntityReferencesResult(
@Nonnull String referenceName
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(it -> new EntityReference(it.getReferencedEntityType(), it.getReferencedPrimaryKey()))
.toArray(EntityReference[]::new);
} else {
return null;
}
};
}
/**
* Creates an implementation of the method returning an integer representing a primary key of referenced entity.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler singleEntityIdResult(
@Nonnull String referenceName,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> resultWrapper.wrap(
() -> {
final Stream references = referenceExtractor.apply(theState.entity(), referenceName);
if (references == null) {
return null;
} else {
final Optional firstReference = references.findFirst();
if (firstReference.isEmpty()) {
return null;
} else {
final ReferenceContract theReference = firstReference.get();
return theReference.getReferencedPrimaryKey();
}
}
}
);
}
/**
* Creates an implementation of the method returning an integer representing a primary key of referenced entity.
*/
@Nonnull
private static ExceptionRethrowingFunction singleEntityIdResult(
@Nonnull String referenceName
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
final Optional firstReference = sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.findFirst();
if (firstReference.isEmpty()) {
return null;
} else {
final ReferenceContract theReference = firstReference.get();
return theReference.getReferencedPrimaryKey();
}
} else {
return null;
}
};
}
/**
* Creates an implementation of the method returning a list of integers representing a primary keys of referenced entities.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler listOfEntityIdsResult(
@Nonnull String referenceName,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(refs -> refs
.map(ReferenceContract::getReferencedPrimaryKey)
.toList()
).orElse(Collections.emptyList())
);
}
/**
* Creates an implementation of the method returning a list of integers representing a primary keys of referenced entities.
*/
@Nonnull
private static ExceptionRethrowingFunction listOfEntityIdsResult(
@Nonnull String referenceName
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(ReferenceContract::getReferencedPrimaryKey)
.toList();
} else {
return Collections.emptyList();
}
};
}
/**
* Creates an implementation of the method returning a set of integers representing a primary keys of referenced entities.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler setOfEntityIdsResult(
@Nonnull String referenceName,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(refs -> refs
.map(ReferenceContract::getReferencedPrimaryKey)
.collect(CollectorUtils.toUnmodifiableLinkedHashSet())
).orElse(Collections.emptySet())
);
}
/**
* Creates an implementation of the method returning a set of integers representing a primary keys of referenced entities.
*/
@Nonnull
private static ExceptionRethrowingFunction setOfEntityIdsResult(
@Nonnull String referenceName
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(ReferenceContract::getReferencedPrimaryKey)
.collect(CollectorUtils.toUnmodifiableLinkedHashSet());
} else {
return Collections.emptySet();
}
};
}
/**
* Creates an implementation of the method returning an array of integers representing a primary keys of referenced entities.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler arrayOfEntityIdsResult(
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
if (itemType.isPrimitive()) {
Assert.isTrue(
int.class.equals(itemType),
() -> "Only array of integers (`int[]`) is supported, but method returns `" + itemType + "[]`!"
);
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(
refs -> refs
.filter(Objects::nonNull)
.mapToInt(ReferenceContract::getReferencedPrimaryKey)
.toArray()
).orElse(null)
);
} else {
//noinspection unchecked
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(
refs -> refs
.filter(Objects::nonNull)
.map(ReferenceContract::getReferencedPrimaryKey)
.map(it -> EvitaDataTypes.toTargetType(it, (Class extends Serializable>) itemType))
.toArray(count -> (Object[]) Array.newInstance(itemType, count))
).orElse(null)
);
}
}
/**
* Creates an implementation of the method returning an array of integers representing a primary keys of referenced entities.
*/
@Nonnull
private static ExceptionRethrowingFunction arrayOfEntityIdsResult(
@Nonnull String referenceName,
@Nonnull Class extends Serializable> itemType
) {
if (itemType.isPrimitive()) {
Assert.isTrue(
int.class.equals(itemType),
() -> "Only array of integers (`int[]`) is supported, but method returns `" + itemType + "[]`!"
);
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.mapToInt(ReferenceContract::getReferencedPrimaryKey)
.toArray();
} else {
return null;
}
};
} else {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(ReferenceContract::getReferencedPrimaryKey)
.map(it -> EvitaDataTypes.toTargetType(it, itemType))
.toArray(count -> (Object[]) Array.newInstance(itemType, count));
} else {
return null;
}
};
}
}
/**
* Creates an implementation of the method returning a single referenced entity wrapped into a custom proxy instance.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Nonnull
private static CurriedMethodContextInvocationHandler singleEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull String referencedEntityType,
@Nonnull Class itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull Function> entityExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> resultWrapper.wrap(
() -> {
final Stream references = referenceExtractor.apply(theState.entity(), referenceName);
if (references == null) {
return null;
} else {
final Optional firstReference = references.findFirst();
if (firstReference.isEmpty()) {
return theState.getReferencedEntityObjectIfPresent(
referencedEntityType, Integer.MIN_VALUE, itemType, ProxyType.REFERENCED_ENTITY
).orElse(null);
} else {
final ReferenceContract theReference = firstReference.get();
final Optional> referencedInstance = theState.getReferencedEntityObjectIfPresent(
referencedEntityType, theReference.getReferencedPrimaryKey(), itemType, ProxyType.REFERENCED_ENTITY
);
if (referencedInstance.isPresent()) {
return referencedInstance.get();
} else {
Assert.isTrue(
theReference.getReferencedEntity().isPresent(),
() -> ContextMissingException.referencedEntityContextMissing(entityName, referenceName)
);
return entityExtractor
.apply((ReferenceDecorator) theReference)
.map(it -> theState.getOrCreateReferencedEntityProxy(itemType, it, ProxyType.REFERENCED_ENTITY))
.orElse(null);
}
}
}
}
);
}
/**
* Creates an implementation of the method returning a single referenced entity wrapped into a custom proxy instance.
*/
@Nonnull
private static ExceptionRethrowingFunction singleEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull Function> entityExtractor,
@Nonnull BiFunction, EntityContract, Object> proxyFactory
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
final Optional firstReference = sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.findFirst();
//noinspection rawtypes,unchecked
return firstReference
.map(
referenceContract -> createProxy(
entityName, referenceName, (Class)itemType, (BiFunction)proxyFactory, referenceContract, entityExtractor
)
)
.orElse(null);
} else {
return null;
}
};
}
/**
* Creates an implementation of the method returning a list of referenced entities wrapped into a custom proxy instances.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler listOfEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull Function> entityExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(
refs -> refs
.map(
it -> createProxy(
entityName, referenceName, itemType,
(type, entity) -> theState.getOrCreateReferencedEntityProxy(type, entity, ProxyType.REFERENCED_ENTITY),
it, entityExtractor
)
)
.filter(Objects::nonNull)
.toList()
).orElse(Collections.emptyList())
);
}
/**
* Creates an implementation of the method returning a list of referenced entities wrapped into a custom proxy instances.
*/
@Nonnull
private static ExceptionRethrowingFunction listOfEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull Function> entityExtractor,
@Nonnull BiFunction, EntityContract, Object> proxyFactory
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
final Collection references = sealedEntity.getReferences(referenceName);
if (references.isEmpty()) {
return Collections.emptyList();
} else {
//noinspection rawtypes,unchecked
return references.stream()
.filter(Droppable::exists)
.map(it -> createProxy(entityName, referenceName, (Class)itemType, (BiFunction)proxyFactory, it, entityExtractor))
.filter(Objects::nonNull)
.toList();
}
} else {
return Collections.emptyList();
}
};
}
/**
* Creates an implementation of the method returning a set of referenced entities wrapped into a custom proxy instances.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler setOfEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull Function> entityExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(
refs -> refs
.map(
it -> createProxy(
entityName, referenceName, itemType,
(type, entity) -> theState.getOrCreateReferencedEntityProxy(type, entity, ProxyType.REFERENCED_ENTITY),
it, entityExtractor
)
)
.filter(Objects::nonNull)
.collect(CollectorUtils.toUnmodifiableLinkedHashSet())
).orElse(Collections.emptySet())
);
}
/**
* Creates an implementation of the method returning a set of referenced entities wrapped into a custom proxy instances.
*/
@Nonnull
private static ExceptionRethrowingFunction setOfEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull Function> entityExtractor,
@Nonnull BiFunction, EntityContract, Object> proxyFactory
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
final Collection references = sealedEntity.getReferences(referenceName);
if (references.isEmpty()) {
return Collections.emptySet();
} else {
//noinspection rawtypes,unchecked
return references.stream()
.filter(Droppable::exists)
.map(it -> createProxy(entityName, referenceName, (Class)itemType, (BiFunction)proxyFactory, it, entityExtractor))
.filter(Objects::nonNull)
.collect(CollectorUtils.toUnmodifiableLinkedHashSet());
}
} else {
return Collections.emptySet();
}
};
}
/**
* Creates an implementation of the method returning an array of referenced entities wrapped into a custom proxy instances.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler arrayOfEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull Function> entityExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) ->
resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(theState.entity(), referenceName))
.map(
refs -> refs
.map(
it -> createProxy(
entityName, referenceName, itemType,
(type, entity) -> theState.getOrCreateReferencedEntityProxy(type, entity, ProxyType.REFERENCED_ENTITY),
it, entityExtractor
)
)
.filter(Objects::nonNull)
.toArray(count -> (Object[]) Array.newInstance(itemType, count))
).orElse(null)
);
}
/**
* Creates an implementation of the method returning an array of referenced entities wrapped into a custom proxy instances.
*/
@Nonnull
private static ExceptionRethrowingFunction arrayOfEntityResult(
@Nonnull String entityName,
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull Function> entityExtractor,
@Nonnull BiFunction, EntityContract, Object> proxyFactory
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
final Collection references = sealedEntity.getReferences(referenceName);
if (references.isEmpty()) {
return null;
} else {
//noinspection rawtypes,unchecked
return references.stream()
.filter(Droppable::exists)
.map(it -> createProxy(entityName, referenceName, (Class)itemType, (BiFunction)proxyFactory, it, entityExtractor))
.filter(Objects::nonNull)
.toArray(count -> (Object[]) Array.newInstance(itemType, count));
}
} else {
return null;
}
};
}
/**
* Creates an implementation of the method returning a single reference wrapped into a custom proxy instance.
*/
@Nonnull
private static ExceptionRethrowingFunction singleReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> mainType,
@Nonnull Class> itemType,
@Nonnull ProxyReferenceFactory proxyReferenceFactory,
@Nonnull Map referencedEntitySchemas
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
final Optional firstReference = sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.findFirst();
return firstReference.map(
referenceContract -> proxyReferenceFactory.createEntityReferenceProxy(
mainType, itemType, sealedEntity, referencedEntitySchemas, referenceContract
)
)
.orElse(null);
} else {
return null;
}
};
}
/**
* Creates an implementation of the method returning a single reference wrapped into a custom proxy instance.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler singleReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> resultWrapper.wrap(
() -> {
final EntityContract entity = theState.entity();
final Stream references = referenceExtractor.apply(entity, referenceName);
if (references == null) {
return null;
} else {
return references.findFirst()
.map(referenceContract -> theState.getOrCreateEntityReferenceProxy(itemType, referenceContract))
.orElse(null);
}
}
);
}
/**
* Creates an implementation of the method returning a single reference by matching its primary key wrapped into
* a custom proxy instance.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler singleReferenceResultById(
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull TriFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> resultWrapper.wrap(
() -> {
final EntityContract entity = theState.entity();
final Integer referencedId = EvitaDataTypes.toTargetType((Serializable) args[0], int.class);
return referenceExtractor.apply(entity, referenceName, referencedId)
.map(referenceContract -> theState.getOrCreateEntityReferenceProxy(itemType, referenceContract))
.orElse(null);
}
);
}
/**
* Creates an implementation of the method returning a list of references wrapped into a custom proxy instances.
*/
@Nonnull
private static ExceptionRethrowingFunction listOfReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> mainType,
@Nonnull Class> itemType,
@Nonnull ProxyReferenceFactory proxyReferenceFactory,
@Nonnull Map referencedEntitySchemas
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(it -> proxyReferenceFactory.createEntityReferenceProxy(mainType, itemType, sealedEntity, referencedEntitySchemas, it))
.toList();
} else {
return Collections.emptyList();
}
};
}
/**
* Creates an implementation of the method returning a list of references wrapped into a custom proxy instances.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler listOfReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> {
final EntityContract entity = theState.entity();
return resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(entity, referenceName))
.map(
refs -> refs
.map(it -> theState.getOrCreateEntityReferenceProxy(itemType, it))
.toList()
).orElse(Collections.emptyList())
);
};
}
/**
* Creates an implementation of the method returning a set of references wrapped into a custom proxy instances.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler setOfReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> {
final EntityContract entity = theState.entity();
return resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(entity, referenceName))
.map(
refs -> refs
.map(it -> theState.getOrCreateEntityReferenceProxy(itemType, it))
.collect(CollectorUtils.toUnmodifiableLinkedHashSet())
).orElse(Collections.emptySet())
);
};
}
/**
* Creates an implementation of the method returning a set of references wrapped into a custom proxy instances.
*/
@Nonnull
private static ExceptionRethrowingFunction setOfReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> mainType,
@Nonnull Class> itemType,
@Nonnull ProxyReferenceFactory proxyReferenceFactory,
@Nonnull Map referencedEntitySchemas
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(it -> proxyReferenceFactory.createEntityReferenceProxy(mainType, itemType, sealedEntity, referencedEntitySchemas, it))
.collect(CollectorUtils.toUnmodifiableLinkedHashSet());
} else {
return Collections.emptySet();
}
};
}
/**
* Creates an implementation of the method returning an array of references wrapped into a custom proxy instances.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler arrayOfReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
return (entityClassifier, theMethod, args, theState, invokeSuper) -> {
final EntityContract entity = theState.entity();
return resultWrapper.wrap(
() -> ofNullable(referenceExtractor.apply(entity, referenceName))
.map(
refs -> refs
.map(it -> theState.getOrCreateEntityReferenceProxy(itemType, it))
.toArray(count -> (Object[]) Array.newInstance(itemType, count))
).orElse(null)
);
};
}
/**
* Creates an implementation of the method returning an array of references wrapped into a custom proxy instances.
*/
@Nonnull
private static ExceptionRethrowingFunction arrayOfReferenceResult(
@Nonnull String referenceName,
@Nonnull Class> mainType,
@Nonnull Class> itemType,
@Nonnull ProxyReferenceFactory proxyReferenceFactory,
@Nonnull Map referencedEntitySchemas
) {
return sealedEntity -> {
if (sealedEntity.referencesAvailable(referenceName)) {
return sealedEntity.getReferences(referenceName)
.stream()
.filter(Droppable::exists)
.map(it -> proxyReferenceFactory.createEntityReferenceProxy(mainType, itemType, sealedEntity, referencedEntitySchemas, it))
.toArray(count -> (Object[]) Array.newInstance(itemType, count));
} else {
return null;
}
};
}
/**
* Method returns implementation of the method returning referenced entities in the simple or complex (custom type)
* form.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler getReferencedEntity(
@Nonnull EntitySchemaContract entitySchema,
@Nonnull ReferenceSchemaContract referenceSchema,
@Nonnull String targetEntityType,
@Nullable Class> collectionType,
@Nonnull Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
// we return directly the referenced entity - either it or its group by matching the entity referenced name
final String entityName = entitySchema.getName();
final String referenceName = referenceSchema.getName();
final String referencedEntityType = referenceSchema.getReferencedEntityType();
final String referencedGroupType = referenceSchema.getReferencedGroupType();
if (targetEntityType.equals(referencedEntityType)) {
Assert.isTrue(
referenceSchema.isReferencedEntityTypeManaged(),
() -> new EntityClassInvalidException(
itemType,
"Entity class type `" + itemType + "` reference `" +
referenceSchema.getName() + "` relates to the entity type `" + referencedEntityType +
"` which is not managed by evitaDB and cannot be wrapped into a proxy class!"
)
);
if (collectionType == null) {
return singleEntityResult(entityName, referenceName, referencedEntityType, itemType, referenceExtractor, ReferenceDecorator::getReferencedEntity, resultWrapper);
} else if (collectionType.isArray()) {
return arrayOfEntityResult(entityName, referenceName, itemType, referenceExtractor, ReferenceDecorator::getReferencedEntity, resultWrapper);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityResult(entityName, referenceName, itemType, referenceExtractor, ReferenceDecorator::getReferencedEntity, resultWrapper);
} else {
return listOfEntityResult(entityName, referenceName, itemType, referenceExtractor, ReferenceDecorator::getReferencedEntity, resultWrapper);
}
} else if (targetEntityType.equals(referencedGroupType)) {
Assert.isTrue(
referenceSchema.isReferencedGroupTypeManaged(),
() -> new EntityClassInvalidException(
itemType,
"Entity class type `" + itemType + "` reference `" +
referenceSchema.getName() + "` relates to the group type `" + referencedGroupType +
"` which is not managed by evitaDB and cannot be wrapped into a proxy class!"
)
);
if (collectionType == null) {
return singleEntityResult(entityName, referenceName, referencedGroupType, itemType, referenceExtractor, ReferenceDecorator::getGroupEntity, resultWrapper);
} else if (collectionType.isArray()) {
return arrayOfEntityResult(entityName, referenceName, itemType, referenceExtractor, ReferenceDecorator::getGroupEntity, resultWrapper);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityResult(entityName, referenceName, itemType, referenceExtractor, ReferenceDecorator::getGroupEntity, resultWrapper);
} else {
return listOfEntityResult(entityName, referenceName, itemType, referenceExtractor, ReferenceDecorator::getGroupEntity, resultWrapper);
}
} else {
throw new EntityClassInvalidException(
itemType,
"Entity class type `" + itemType + "` is not compatible with reference `" +
referenceSchema.getName() + "` of entity type `" + referencedEntityType +
"` or group type `" + referencedGroupType + "`!"
);
}
}
/**
* Method returns implementation of the method returning referenced entities in the simple or complex (custom type)
* form.
*/
@Nonnull
private static ExceptionRethrowingFunction getReferencedEntity(
@Nonnull EntitySchemaContract entitySchema,
@Nonnull ReferenceSchemaContract referenceSchema,
@Nonnull String targetEntityName,
@Nullable Class> collectionType,
@Nonnull Class> itemType,
@Nonnull BiFunction, EntityContract, Object> proxyFactory
) {
// we return directly the referenced entity - either it or its group by matching the entity referenced name
final String entityName = entitySchema.getName();
final String referenceName = referenceSchema.getName();
final String referencedEntityType = referenceSchema.getReferencedEntityType();
final String referencedGroupType = referenceSchema.getReferencedGroupType();
if (targetEntityName.equals(referencedEntityType)) {
Assert.isTrue(
referenceSchema.isReferencedEntityTypeManaged(),
() -> new EntityClassInvalidException(
itemType,
"Entity class type `" + itemType + "` reference `" +
referenceSchema.getName() + "` relates to the entity type `" + referencedEntityType +
"` which is not managed by evitaDB and cannot be wrapped into a proxy class!"
)
);
if (collectionType == null) {
return singleEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getReferencedEntity, proxyFactory);
} else if (collectionType.isArray()) {
return arrayOfEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getReferencedEntity, proxyFactory);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getReferencedEntity, proxyFactory);
} else {
return listOfEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getReferencedEntity, proxyFactory);
}
} else if (targetEntityName.equals(referencedGroupType)) {
Assert.isTrue(
referenceSchema.isReferencedGroupTypeManaged(),
() -> new EntityClassInvalidException(
itemType,
"Entity class type `" + itemType + "` reference `" +
referenceSchema.getName() + "` relates to the group type `" + referencedGroupType +
"` which is not managed by evitaDB and cannot be wrapped into a proxy class!"
)
);
if (collectionType == null) {
return singleEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getGroupEntity, proxyFactory);
} else if (collectionType.isArray()) {
return arrayOfEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getGroupEntity, proxyFactory);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getGroupEntity, proxyFactory);
} else {
return listOfEntityResult(entityName, referenceName, itemType, ReferenceDecorator::getGroupEntity, proxyFactory);
}
} else {
throw new EntityClassInvalidException(
itemType,
"Entity class type `" + itemType + "` is not compatible with reference `" +
referenceSchema.getName() + "` of entity type `" + referencedEntityType +
"` or group type `" + referencedGroupType + "`!"
);
}
}
/**
* Method returns implementation of the method returning referenced entities in the form of {@link EntityReference}.
*/
@Nonnull
private static ExceptionRethrowingFunction getEntityReference(
@Nonnull String referenceName,
@Nullable Class> collectionType
) {
if (collectionType == null) {
return singleEntityReferenceResult(referenceName);
} else if (collectionType.isArray()) {
return arrayOfEntityReferencesResult(referenceName);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityReferencesResult(referenceName);
} else {
return listOfEntityReferencesResult(referenceName);
}
}
/**
* Method returns implementation of the method returning referenced entities in the form of {@link EntityReference}.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler getEntityReference(
@Nonnull String referenceName,
@Nullable Class> collectionType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
if (collectionType == null) {
return singleEntityReferenceResult(referenceName, referenceExtractor, resultWrapper);
} else if (collectionType.isArray()) {
return arrayOfEntityReferencesResult(referenceName, referenceExtractor, resultWrapper);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityReferencesResult(referenceName, referenceExtractor, resultWrapper);
} else {
return listOfEntityReferencesResult(referenceName, referenceExtractor, resultWrapper);
}
}
/**
* Method returns implementation of the method returning referenced entities in the form of integer primary key.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler getEntityId(
@Nonnull String referenceName,
@Nullable Class> collectionType,
@Nullable Class> itemType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull ResultWrapper resultWrapper
) {
if (collectionType == null) {
return singleEntityIdResult(referenceName, referenceExtractor, resultWrapper);
} else if (collectionType.isArray()) {
return arrayOfEntityIdsResult(referenceName, itemType, referenceExtractor, resultWrapper);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityIdsResult(referenceName, referenceExtractor, resultWrapper);
} else {
return listOfEntityIdsResult(referenceName, referenceExtractor, resultWrapper);
}
}
/**
* Method returns implementation of the method returning referenced entities in the form of integer primary key.
*/
@Nonnull
private static ExceptionRethrowingFunction getEntityId(
@Nonnull String referenceName,
@Nullable Class> collectionType,
@Nullable Class extends Serializable> itemType
) {
if (collectionType == null) {
return singleEntityIdResult(referenceName);
} else if (collectionType.isArray()) {
return arrayOfEntityIdsResult(referenceName, itemType);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfEntityIdsResult(referenceName);
} else {
return listOfEntityIdsResult(referenceName);
}
}
/**
* Method returns implementation of the method returning references entities in the form of custom proxied types.
*/
@Nonnull
private static CurriedMethodContextInvocationHandler getReference(
@Nonnull String referenceName,
@Nullable Class> collectionType,
@Nonnull BiFunction> referenceExtractor,
@Nonnull Class> itemType,
@Nonnull ResultWrapper resultWrapper
) {
if (collectionType == null) {
return singleReferenceResult(referenceName, itemType, referenceExtractor, resultWrapper);
} else if (collectionType.isArray()) {
return arrayOfReferenceResult(referenceName, itemType, referenceExtractor, resultWrapper);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfReferenceResult(referenceName, itemType, referenceExtractor, resultWrapper);
} else {
return listOfReferenceResult(referenceName, itemType, referenceExtractor, resultWrapper);
}
}
/**
* Method returns implementation of the method returning references entities in the form of custom proxied types.
*/
@Nonnull
private static ExceptionRethrowingFunction getReference(
@Nonnull Map referencedEntitySchemas,
@Nonnull String referenceName,
@Nullable Class> collectionType,
@Nonnull Class> mainType,
@Nonnull Class> itemType,
@Nonnull ProxyReferenceFactory proxyReferenceFactory
) {
if (collectionType == null) {
return singleReferenceResult(referenceName, mainType, itemType, proxyReferenceFactory, referencedEntitySchemas);
} else if (collectionType.isArray()) {
return arrayOfReferenceResult(referenceName, mainType, itemType, proxyReferenceFactory, referencedEntitySchemas);
} else if (Set.class.isAssignableFrom(collectionType)) {
return setOfReferenceResult(referenceName, mainType, itemType, proxyReferenceFactory, referencedEntitySchemas);
} else {
return listOfReferenceResult(referenceName, mainType, itemType, proxyReferenceFactory, referencedEntitySchemas);
}
}
public GetReferenceMethodClassifier() {
super(
"getReference",
(method, proxyState) -> {
@SuppressWarnings("rawtypes") final Class returnType = method.getReturnType();
// we are interested only in abstract methods without parameters
if (returnType.equals(proxyState.getProxyClass()) ||
void.class.equals(returnType) ||
method.isAnnotationPresent(CreateWhenMissing.class) ||
Arrays.stream(method.getParameterAnnotations()).flatMap(Arrays::stream).anyMatch(CreateWhenMissing.class::isInstance) ||
method.isAnnotationPresent(RemoveWhenExists.class) ||
Arrays.stream(method.getParameterAnnotations()).flatMap(Arrays::stream).anyMatch(RemoveWhenExists.class::isInstance)
) {
return null;
}
// now we need to identify reference schema that is being requested
final ReflectionLookup reflectionLookup = proxyState.getReflectionLookup();
final EntitySchemaContract entitySchema = proxyState.getEntitySchema();
final ReferenceSchemaContract referenceSchema = getReferenceSchema(
method, reflectionLookup, entitySchema
);
// if not found, this method is not classified by this implementation
if (referenceSchema == null) {
return null;
} else {
// finally provide implementation that will retrieve the reference or reference entity from the entity
final String referenceName = referenceSchema.getName();
// now we need to identify the return type
final Class>[] resolvedTypes = getResolvedTypes(method, proxyState.getProxyClass());
final ResultWrapper resultWrapper = ProxyUtils.createOptionalWrapper(
method, Optional.class.isAssignableFrom(resolvedTypes[0]) ? Optional.class : null
);
final int index = Optional.class.isAssignableFrom(resolvedTypes[0]) ? 1 : 0;
final Class> collectionType;
final Class> itemType;
if (Collection.class.equals(resolvedTypes[index]) || List.class.isAssignableFrom(resolvedTypes[index]) || Set.class.isAssignableFrom(resolvedTypes[index])) {
collectionType = resolvedTypes[index];
itemType = resolvedTypes.length > index + 1 ? resolvedTypes[index + 1] : EntityReference.class;
} else if (resolvedTypes[index].isArray()) {
collectionType = resolvedTypes[index];
itemType = returnType.getComponentType();
} else {
collectionType = null;
itemType = resolvedTypes[index];
}
@Nonnull final BiFunction> referenceExtractor =
resultWrapper instanceof OptionalProducingOperator ?
(entity, theReferenceName) -> entity.referencesAvailable(theReferenceName) ?
entity.getReferences(theReferenceName).stream().filter(Droppable::exists) : null :
(theEntity, theRefName) -> theEntity.getReferences(theRefName).stream().filter(Droppable::exists);
// return the appropriate result
final Entity entityInstance = reflectionLookup.getClassAnnotation(itemType, Entity.class);
final EntityRef entityRefInstance = reflectionLookup.getClassAnnotation(itemType, EntityRef.class);
if (EntityReferenceContract.class.isAssignableFrom(itemType)) {
return getEntityReference(referenceName, collectionType, referenceExtractor, resultWrapper);
} else if (Number.class.isAssignableFrom(EvitaDataTypes.toWrappedForm(itemType))) {
return getEntityId(referenceName, collectionType, itemType, referenceExtractor, resultWrapper);
} else if (entityInstance != null) {
return getReferencedEntity(
entitySchema, referenceSchema,
entityInstance.name(), collectionType, itemType, referenceExtractor, resultWrapper
);
} else if (entityRefInstance != null) {
return getReferencedEntity(
entitySchema, referenceSchema,
entityRefInstance.value(), collectionType, itemType, referenceExtractor, resultWrapper
);
} else if (method.getParameterCount() == 1 && NumberUtils.isIntConvertibleNumber(method.getParameterTypes()[0]) && collectionType == null) {
@Nonnull final TriFunction> referenceByIdExtractor =
resultWrapper instanceof OptionalProducingOperator ?
(entity, theReferenceName, referencedEPK) -> entity.referencesAvailable(theReferenceName) ?
entity.getReference(theReferenceName, referencedEPK).filter(Droppable::exists) : empty() :
(theEntity, theRefName, referencedEPK) -> theEntity.getReference(theRefName, referencedEPK).filter(Droppable::exists);
return singleReferenceResultById(
referenceName, itemType, referenceByIdExtractor, resultWrapper
);
} else {
return getReference(
referenceName, collectionType, referenceExtractor, itemType, resultWrapper
);
}
}
}
);
}
}