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

org.seedstack.business.internal.assembler.dsl.LegacyDtoInfoResolver Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2013-2024, The SeedStack authors 
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.business.internal.assembler.dsl;

import static org.seedstack.shed.reflect.ReflectUtils.makeAccessible;

import java.lang.annotation.Annotation;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Priority;
import javax.inject.Inject;
import org.seedstack.business.assembler.MatchingEntityId;
import org.seedstack.business.assembler.MatchingFactoryParameter;
import org.seedstack.business.domain.AggregateRoot;
import org.seedstack.business.domain.DomainRegistry;
import org.seedstack.business.internal.BusinessErrorCode;
import org.seedstack.business.internal.BusinessException;
import org.seedstack.business.spi.BaseDtoInfoResolver;
import org.seedstack.business.spi.DtoInfoResolver;
import org.seedstack.business.spi.DtoInfoResolverPriority;
import org.seedstack.shed.cache.Cache;
import org.seedstack.shed.cache.CacheParameters;
import org.seedstack.shed.reflect.Classes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of the {@link DtoInfoResolver} based on the {@link MatchingEntityId} and
 * {@link MatchingFactoryParameter} annotation.
 *
 * @see MatchingEntityId
 * @see MatchingFactoryParameter
 */
@Priority(DtoInfoResolverPriority.MATCHING_ANNOTATIONS)
class LegacyDtoInfoResolver extends BaseDtoInfoResolver {
    private static final Logger LOGGER = LoggerFactory.getLogger(LegacyDtoInfoResolver.class);
    private static final Class MATCHING_ENTITY_ID = MatchingEntityId.class;
    private static final Class MATCHING_FACT_PARAM = MatchingFactoryParameter.class;
    private static final Cache, DtoInfo> dtoInfo = Cache.create(
            new CacheParameters, DtoInfo>()
                    .setInitialSize(256)
                    .setMaxSize(1024)
                    .setLoadingFunction(LegacyDtoInfoResolver::resolveDtoInfo)
    );

    @Inject
    LegacyDtoInfoResolver(DomainRegistry domainRegistry) {
        super(domainRegistry);
    }

    private static  DtoInfo resolveDtoInfo(Class dtoClass) {
        final ParameterHolder idParameterHolder = new ParameterHolder<>((Class) dtoClass);
        final ParameterHolder aggregateParameterHolder = new ParameterHolder<>((Class) dtoClass);
        final AtomicBoolean supported = new AtomicBoolean(false);

        LOGGER.debug("Resolving DTO information on {}", dtoClass);

        Classes.from(dtoClass)
                .traversingSuperclasses()
                .methods()
                .forEach(method -> {
                    makeAccessible(method);

                    MatchingEntityId idAnnotation = method.getAnnotation(MatchingEntityId.class);
                    if (idAnnotation != null) {
                        if (idAnnotation.typeIndex() >= 0) {
                            if (idAnnotation.index() >= 0) {
                                idParameterHolder.addTupleParameter(MATCHING_ENTITY_ID,
                                        idAnnotation.typeIndex(), idAnnotation.index(), method);
                            } else {
                                idParameterHolder.addTupleValue(MATCHING_ENTITY_ID, idAnnotation.typeIndex(),
                                        method);
                            }
                        } else {
                            if (idAnnotation.index() >= 0) {
                                idParameterHolder.addParameter(MATCHING_ENTITY_ID, idAnnotation.index(), method);
                            } else {
                                idParameterHolder.addValue(MATCHING_ENTITY_ID, method);
                            }
                        }
                        supported.set(true);
                    }

                    MatchingFactoryParameter factoryAnnotation = method
                            .getAnnotation(MatchingFactoryParameter.class);
                    if (factoryAnnotation != null) {
                        if (factoryAnnotation.typeIndex() >= 0) {
                            if (factoryAnnotation.index() >= 0) {
                                aggregateParameterHolder.addTupleParameter(MATCHING_FACT_PARAM,
                                        factoryAnnotation.typeIndex(), factoryAnnotation.index(), method);
                            } else {
                                aggregateParameterHolder.addTupleValue(MATCHING_FACT_PARAM,
                                        factoryAnnotation.typeIndex(), method);
                            }
                        } else {
                            if (factoryAnnotation.index() >= 0) {
                                aggregateParameterHolder.addParameter(MATCHING_FACT_PARAM,
                                        factoryAnnotation.index(), method);
                            } else {
                                aggregateParameterHolder.addValue(MATCHING_FACT_PARAM, method);
                            }
                        }
                        supported.set(true);
                    }
                });
        if (supported.get()) {
            return new DtoInfo<>(true, idParameterHolder.freeze(), aggregateParameterHolder.freeze());
        } else {
            return new DtoInfo<>(false, null, null);
        }
    }

    @Override
    public  boolean supports(D dto) {
        return getDtoInfo(dto).supported;
    }

    @Override
    public  I resolveId(D dto, Class aggregateIdClass) {
        ParameterHolder parameterHolder = getIdParameterHolder(dto, aggregateIdClass);
        return createIdentifier(aggregateIdClass, parameterHolder.uniqueElement(dto),
                parameterHolder.parameters(dto));
    }

    @Override
    public  I resolveId(D dto, Class aggregateIdClass, int position) {
        ParameterHolder parameterHolder = getIdParameterHolder(dto, aggregateIdClass);
        return createIdentifier(aggregateIdClass, parameterHolder.uniqueElementForAggregate(dto, position),
                parameterHolder.parametersOfAggregateRoot(dto, position));
    }

    @Override
    public > A resolveAggregate(D dto, Class aggregateRootClass) {
        return createFromFactory(aggregateRootClass, getAggregateParameterHolder(dto).parameters(dto));
    }

    @Override
    public > A resolveAggregate(D dto, Class aggregateRootClass, int position) {
        return createFromFactory(aggregateRootClass,
                getAggregateParameterHolder(dto).parametersOfAggregateRoot(dto, position));
    }

    private  ParameterHolder getIdParameterHolder(D dto, Class aggregateIdClass) {
        ParameterHolder parameterHolder = getDtoInfo(dto).idParameterHolder;
        if (parameterHolder.isEmpty()) {
            throw BusinessException.createNew(BusinessErrorCode.NO_IDENTITY_CAN_BE_RESOLVED_FROM_DTO)
                    .put("dtoClass", dto.getClass()
                            .getName())
                    .put("aggregateIdClass", aggregateIdClass);
        }
        return parameterHolder;
    }

    private  ParameterHolder getAggregateParameterHolder(D dto) {
        return getDtoInfo(dto).aggregateParameterHolder;
    }

    @SuppressWarnings("unchecked")
    private  DtoInfo getDtoInfo(D dto) {
        return (DtoInfo) dtoInfo.get(dto.getClass());
    }

    private static class DtoInfo {
        final boolean supported;
        final ParameterHolder idParameterHolder;
        final ParameterHolder aggregateParameterHolder;

        private DtoInfo(boolean supported, ParameterHolder idParameterHolder,
                ParameterHolder aggregateParameterHolder) {
            this.supported = supported;
            this.idParameterHolder = idParameterHolder;
            this.aggregateParameterHolder = aggregateParameterHolder;
        }
    }
}