minium.actions.internal.AbstractInteraction.java Source code

Java tutorial

Introduction

Here is the source code for minium.actions.internal.AbstractInteraction.java

Source

/*
 * Copyright (C) 2015 The Minium Authors
 *
 * 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 minium.actions.internal;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.List;
import java.util.Set;

import minium.Elements;
import minium.FreezableElements;
import minium.IterableElements;
import minium.actions.Configuration;
import minium.actions.Configuration.WaitingPreset;
import minium.actions.Duration;
import minium.actions.HasConfiguration;
import minium.actions.Interaction;
import minium.actions.InteractionEvent;
import minium.actions.InteractionEvent.Type;
import minium.actions.InteractionListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * The Class DefaultInteraction.
 */
public abstract class AbstractInteraction implements Interaction {

    private static final Logger logger = LoggerFactory.getLogger(AbstractInteraction.class);

    private final Set<InteractionListener> listeners = Sets.newLinkedHashSet();
    private final Elements originalSource;
    private final boolean canFreeze;
    private Elements source;
    private String preset;

    public AbstractInteraction(Elements elems) {
        this(elems, true);
    }

    public AbstractInteraction(Elements elems, boolean canFreeze) {
        originalSource = elems;
        this.canFreeze = canFreeze;
        refreeze();
    }

    public void refreeze() {
        if (originalSource != null) {
            this.source = originalSource.is(FreezableElements.class) && canFreeze
                    ? originalSource.as(FreezableElements.class).freeze()
                    : originalSource;
        }
    }

    public void setWaitingPreset(String preset) {
        this.preset = preset;
    }

    public String getWaitingPreset() {
        return preset;
    }

    @Override
    public void waitToPerform() {
        waitFor(WaitPredicates.forExistence());
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vilt.minium.actions.Interaction#perform()
     */
    @Override
    public void perform() {
        perform(true);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vilt.minium.actions.Interaction#registerListener(com.vilt.minium.actions.InteractionListener)
     */
    @Override
    public void registerListener(InteractionListener listener) {
        listeners.add(listener);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vilt.minium.actions.Interaction#unregisterListener(com.vilt.minium.actions.InteractionListener)
     */
    @Override
    public void unregisterListener(InteractionListener listener) {
        listeners.remove(listener);
    }

    protected void waitFor(Predicate<? super Elements> predicate) {
        if (source != null) {
            wait(source, preset, predicate);
        }
    }

    protected void waitOrTimeoutFor(Predicate<? super Elements> predicate) {
        if (source != null) {
            waitOrTimeout(source, preset, predicate);
        }
    }

    /**
     * Gets the source.
     *
     * @return the source
     */
    protected Elements getSource() {
        return source;
    }

    /**
     * Do perform.
     */
    protected abstract void doPerform();

    /**
     * Gets the first.
     *
     * @param elems
     *            the elems
     * @return the first
     */
    protected Elements getFirst(Elements elems) {
        @SuppressWarnings("unchecked")
        IterableElements<Elements> iterableElems = elems.as(IterableElements.class);
        return Iterables.getFirst(iterableElems, null);
    }

    /**
     * Trigger.
     *
     * @param type
     *            the type
     */
    protected boolean trigger(InteractionEvent.Type type, Throwable e) {
        return trigger(getAllListeners(), type, e);
    }

    /**
     * Trigger reverse.
     *
     * @param type
     *            the type
     */
    protected boolean triggerReverse(Type type, Throwable e) {
        return trigger(Lists.reverse(getAllListeners()), type, e);
    }

    /**
     * Trigger.
     *
     * @param listeners
     *            the listeners
     * @param type
     *            the type
     */
    protected boolean trigger(List<InteractionListener> listeners, Type type, Throwable e) {
        if (listeners.isEmpty())
            return false;
        InteractionEvent event = createInteractionEvent(type, e);
        for (InteractionListener listener : listeners) {
            listener.onEvent(event);
        }
        if (event instanceof AfterFailInteractionEvent)
            return ((AfterFailInteractionEvent) event).isRetry();
        return false;
    }

    private void perform(boolean canRetry) {
        trigger(Type.BEFORE_WAIT, null);
        try {
            waitToPerform();
            trigger(Type.BEFORE, null);
            doPerform();
            triggerReverse(Type.AFTER_SUCCESS, null);
        } catch (RuntimeException e) {
            // always call after fail
            boolean retry = triggerReverse(Type.AFTER_FAIL, e);

            // we first need to check if thread has been interrupted
            if (Thread.interrupted()) {
                Thread.currentThread().interrupt();
                throw e;
            }

            // otherwise, retry if the case
            if (retry && canRetry) {
                logger.debug("Interaction was marked as retriable, let's retry it");
                perform(false);
            } else {
                throw e;
            }
        }
    }

    protected InteractionEvent createInteractionEvent(Type type, Throwable e) {
        checkNotNull(type);
        switch (type) {
        case BEFORE_WAIT:
            return new BeforeWaitInteractionEvent(source, this);
        case BEFORE:
            return new BeforeInteractionEvent(source, this);
        case AFTER_FAIL:
            return new AfterFailInteractionEvent(source, this, e);
        case AFTER_SUCCESS:
            return new AfterSuccessInteractionEvent(source, this);
        }

        // shouldn't occurr
        throw new IllegalArgumentException("Type must be not null and valid");
    }

    private List<InteractionListener> getAllListeners() {
        List<InteractionListener> allListeners = Lists.newArrayList();
        if (source != null && source.is(HasConfiguration.class)) {
            Iterable<InteractionListener> globalListeners = source.as(HasConfiguration.class).configure()
                    .interactionListeners();
            allListeners.addAll(Lists.newArrayList(globalListeners));
        }
        allListeners.addAll(listeners);

        return allListeners;
    }

    protected void wait(Elements webElements, String preset, Predicate<? super Elements> predicate) {
        WaitingPreset waitingPreset = configure().waitingPreset(preset);
        wait(webElements, waitingPreset.timeout(), waitingPreset.interval(), predicate);
    }

    protected void wait(Elements elems, Duration timeout, Duration interval,
            Predicate<? super Elements> predicate) {
        Waits.waitForPredicate(elems, timeout, interval, predicate);
    }

    protected void waitOrTimeout(Elements webElements, String preset, Predicate<? super Elements> predicate) {
        WaitingPreset waitingPreset = configure().waitingPreset(preset);
        waitOrTimeout(webElements, waitingPreset.timeout(), waitingPreset.interval(), predicate);
    }

    protected void waitOrTimeout(Elements elems, Duration timeout, Duration interval,
            Predicate<? super Elements> predicate) {
        Waits.waitForPredicateOrTimeout(elems, timeout, interval, predicate);
    }

    private Configuration configure() {
        return getSource().as(HasConfiguration.class).configure();
    }
}