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

org.fluentlenium.core.conditions.wait.WaitConditionInvocationHandler Maven / Gradle / Ivy

package org.fluentlenium.core.conditions.wait;

import org.fluentlenium.core.FluentControl;
import org.fluentlenium.core.conditions.Conditions;
import org.fluentlenium.core.conditions.ConditionsObject;
import org.fluentlenium.core.conditions.Negation;
import org.fluentlenium.core.conditions.message.MessageContext;
import org.fluentlenium.core.conditions.message.MessageProxy;
import org.fluentlenium.core.wait.FluentWait;
import org.openqa.selenium.WrapsElement;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Invocation handler used to wait for a particular conditions call.
 *
 * @param  type of conditions
 */
public class WaitConditionInvocationHandler> implements InvocationHandler {

    private final Class conditionClass;
    private final Supplier conditionSupplier;
    private final FluentWait wait;
    private String context;
    private boolean negation;

    /**
     * Creates a new wait condition invocation handler.
     *
     * @param conditionClass    condition class
     * @param wait              fluent wait
     * @param context           base context of generated message if condition is not verified
     * @param conditionSupplier supplier of conditions
     */
    public WaitConditionInvocationHandler(Class conditionClass, FluentWait wait, String context,
            Supplier conditionSupplier) {
        this.conditionClass = conditionClass;
        this.wait = wait;
        this.context = context;
        this.conditionSupplier = conditionSupplier;
    }

    /**
     * Get the underlying conditions of wait matcher.
     *
     * @return underlying conditions.
     */
    protected C conditions() {
        return conditions(false);
    }

    /**
     * Get the underlying conditions of wait matcher.
     *
     * @param ignoreNot true if the negation should be ignored.
     * @return underlying conditions.
     */
    protected C conditions(boolean ignoreNot) {
        C conditions = conditionSupplier.get();
        return applyNegation(conditions, ignoreNot);
    }

    /**
     * Apply the current negation to the given condition
     *
     * @param conditions     conditions.
     * @param ignoreNegation true if the negation should be ignored.
     * @return conditions with the negation applied.
     */
    protected C applyNegation(C conditions, boolean ignoreNegation) {
        if (!ignoreNegation && negation) {
            return (C) conditions.not();
        }
        return conditions;
    }

    /**
     * Builds a message builder proxy.
     *
     * @return message builder proxy
     */
    protected C messageBuilder() {
        return messageBuilder(false);
    }

    /**
     * Builds a message builder proxy.
     *
     * @param ignoreNegation true if the negation should be ignored.
     * @return message builder proxy
     */
    protected C messageBuilder(boolean ignoreNegation) {
        C conditions = MessageProxy.builder(conditionClass, context);
        conditions = applyNegation(conditions, ignoreNegation);
        return conditions;
    }

    /**
     * Build the final message from default message.
     *
     * @return final message
     */
    protected Function messageCustomizer() {
        return Function.identity();
    }

    /**
     * Perform the wait.
     *
     * @param present predicate to wait for.
     * @param message message to use.
     */
    protected void until(Predicate present, String message) {
        if (wait.hasMessageDefined()) {
            wait.untilPredicate(present);
        } else {
            message = messageCustomizer().apply(message);
            wait.withMessage(message).untilPredicate(present);
        }
    }

    /**
     * Perform the wait.
     *
     * @param present         predicate to wait for.
     * @param messageSupplier default message to use.
     */
    protected void until(Predicate present, Supplier messageSupplier) {
        if (wait.hasMessageDefined()) {
            wait.untilPredicate(present);
        } else {
            Supplier customMessageSupplier = () -> messageCustomizer().apply(messageSupplier.get());
            wait.withMessage(customMessageSupplier).untilPredicate(present);
        }
    }

    /**
     * Perform the wait.
     *
     * @param condition         condition object to wait for
     * @param messageBuilder    message builder matching the condition object
     * @param conditionFunction condition function
     */
    protected void until(C condition, C messageBuilder, Function conditionFunction) {
        Predicate predicate = input -> conditionFunction.apply(condition);
        Supplier messageSupplier = () -> {
            conditionFunction.apply(messageBuilder);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(MessageProxy.message(messageBuilder));

            if (condition instanceof ConditionsObject) {
                Object actualObject = ((ConditionsObject) condition).getActualObject();

                if (!(actualObject instanceof WrapsElement)) {
                    stringBuilder.append(" (actual: ").append(actualObject).append(')');
                }
            }

            return stringBuilder.toString();
        };

        until(predicate, messageSupplier);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.isAnnotationPresent(Negation.class)) {
            return buildNegationProxy();
        }

        if (method.isAnnotationPresent(MessageContext.class)) {
            context = context + " " + method.getAnnotation(MessageContext.class).value();
        }

        Class returnType = method.getReturnType();
        if (boolean.class.equals(returnType) || Boolean.class.equals(returnType)) {
            return waitForCondition(method, args);
        } else if (Conditions.class.isAssignableFrom(returnType)) {
            return buildChildProxy(method, args);
        } else {
            throw new IllegalStateException("An internal error has occurred.");
        }
    }

    private Object buildChildProxy(Method method, Object[] args)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method conditionGetter = conditions().getClass().getMethod(method.getName(), method.getParameterTypes());
        Conditions childConditions = (Conditions) conditionGetter.invoke(conditions(true), args);

        Conditions childProxy = WaitConditionProxy
                .custom((Class>) method.getReturnType(), wait, context, () -> childConditions);
        WaitConditionInvocationHandler childHandler = (WaitConditionInvocationHandler) Proxy.getInvocationHandler(childProxy);
        childHandler.negation = negation;
        return childProxy;
    }

    private boolean waitForCondition(Method method, Object[] args) {
        C messageBuilder = messageBuilder();
        until(conditions(), messageBuilder, input -> {
            try {
                return (Boolean) method.invoke(input, args);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("An internal error has occured while waiting", e);
            } catch (InvocationTargetException e) {
                Throwable targetException = e.getTargetException();
                if (targetException instanceof RuntimeException) {
                    throw (RuntimeException) targetException;
                }
                throw new IllegalStateException("An internal error has occured while waiting", e);
            }
        });
        return true;
    }

    private Conditions buildNegationProxy() {
        Conditions negationProxy = WaitConditionProxy.custom(conditionClass, wait, context, conditionSupplier);
        WaitConditionInvocationHandler negationHandler = (WaitConditionInvocationHandler) Proxy
                .getInvocationHandler(negationProxy);
        negationHandler.negation = !negation;
        return negationProxy;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy