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

tech.sirwellington.alchemy.arguments.AssertionBuilderImpl Maven / Gradle / Ivy

Go to download

Part of the Alchemy Collection. Easy, Simple, and Robust argument checking logic for your Services, Libraries, and Scripts.

There is a newer version: 2.3
Show newest version
/*
 * Copyright 2015 SirWellington Tech.
 *
 * 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 tech.sirwellington.alchemy.arguments;

import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.sirwellington.alchemy.annotations.access.Internal;
import tech.sirwellington.alchemy.annotations.concurrency.Immutable;
import tech.sirwellington.alchemy.annotations.designs.FluidAPIDesign;
import tech.sirwellington.alchemy.annotations.designs.patterns.StrategyPattern;

import static tech.sirwellington.alchemy.annotations.designs.patterns.StrategyPattern.Role.CLIENT;
import static tech.sirwellington.alchemy.arguments.Checks.Internal.isNullOrEmpty;
import static tech.sirwellington.alchemy.arguments.ExceptionMapper.IDENTITY;

/**
 *
 * @author SirWellington
 */
@FluidAPIDesign
@StrategyPattern(role = CLIENT)
@Immutable
@Internal
final class AssertionBuilderImpl implements AssertionBuilder
{

    private final static Logger LOG = LoggerFactory.getLogger(AssertionBuilderImpl.class);

    private final AlchemyAssertion assertion;
    private final ExceptionMapper exceptionMapper;
    @Immutable
    private final List arguments;
    private final String overrideMessage;

    private AssertionBuilderImpl(AlchemyAssertion assertion,
                                 ExceptionMapper exceptionMapper,
                                 String overrideMessage,
                                 List arguments)
    {
        this.assertion = assertion;
        this.exceptionMapper = exceptionMapper;
        this.overrideMessage = overrideMessage;
        this.arguments = arguments;
    }

    @Override
    public AssertionBuilder usingMessage(String message)
    {
        Checks.Internal.checkThat(!isNullOrEmpty(message), "error message is empty");
        
        ExceptionMapper newExceptionMapper;
        if(exceptionMapper instanceof  DynamicExceptionSupplier)
        {
            newExceptionMapper = createUpdatedDynamicExceptionMapperWithMessage(message);
        }
        else
        {
            newExceptionMapper = this.exceptionMapper;
        }
        
        return new AssertionBuilderImpl<>(assertion, newExceptionMapper, message, arguments);
    }

    static  AssertionBuilderImpl checkThat(List arguments)
    {
        return new AssertionBuilderImpl<>(null, IDENTITY, "", arguments);
    }

    @Override
    public  AssertionBuilderImpl throwing(ExceptionMapper exceptionMapper)
    {
        Checks.Internal.checkNotNull(exceptionMapper, "exceptionMapper is null");

        return new AssertionBuilderImpl<>(null, exceptionMapper, overrideMessage, arguments);
    }

    @Override
    public  AssertionBuilder throwing(Class exceptionClass)
    {
        Checks.Internal.checkNotNull(exceptionClass);
        
        return this.throwing(new DynamicExceptionSupplier<>(exceptionClass, overrideMessage));
    }
    
    @Override
    public AssertionBuilderImpl is(AlchemyAssertion assertion) throws Ex
    {
        Checks.Internal.checkNotNull(assertion, "assertion is null");

        AssertionBuilderImpl newBuilder = new AssertionBuilderImpl<>(assertion, exceptionMapper, overrideMessage, arguments);

        //Check this assertion
        newBuilder.checkAssertion();
        //Return the new one to allow further assertions on this argument
        return newBuilder;
    }

    private void checkAssertion() throws Ex
    {
        Checks.Internal.checkState(assertion != null, "no assertion found");
        Checks.Internal.checkState(exceptionMapper != null, "no exceptionMapper found");

        FailedAssertionException caught = null;

        try
        {
            arguments.forEach(assertion::check);
        }
        catch (FailedAssertionException ex)
        {
            caught = ex;
            if (!Checks.Internal.isNullOrEmpty(overrideMessage))
            {
                caught.changeMessage(overrideMessage);
            }
        }
        catch (RuntimeException ex)
        {
            handleUnexpectedException(ex);
        }

        if (exceptionOccured(caught))
        {
            handleFailedAssertion(caught);
        }

    }

    private boolean exceptionOccured(FailedAssertionException caught)
    {
        return caught != null;
    }

    private void handleUnexpectedException(RuntimeException ex) throws Ex
    {
        LOG.warn("Assertion {} threw an unexpected exception. Only {} Exceptions are acceptable for Assertions.",
                 assertion,
                 FailedAssertionException.class.getSimpleName(),
                 ex);

        FailedAssertionException wrappedException = new FailedAssertionException("wrapping unexpected exception", ex);
        handleFailedAssertion(wrappedException);
    }

    private void handleFailedAssertion(FailedAssertionException caught) throws Ex
    {
        Ex mappedEx = exceptionMapper.apply(caught);

        if (mappedEx != null)
        {
            throw mappedEx;
        }
        else
        {
            LOG.warn("Exception Mapper did not return a throwable. Swallowing exception", caught);
        }
    }

    private ExceptionMapper createUpdatedDynamicExceptionMapperWithMessage(String message)
    {
        DynamicExceptionSupplier dynamicExceptionMapper = (DynamicExceptionSupplier) exceptionMapper;
        Class exceptionClass = dynamicExceptionMapper.getExceptionClass();
        return new DynamicExceptionSupplier<>(exceptionClass, message);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy