001package org.junit.rules;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006
007import org.junit.runner.Description;
008import org.junit.runners.model.Statement;
009
010/**
011 * The RuleChain rule allows ordering of TestRules. You create a
012 * {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
013 * {@link #around(TestRule)}:
014 *
015 * <pre>
016 * public static class UseRuleChain {
017 *      &#064;Rule
018 *      public RuleChain chain= RuleChain
019 *                             .outerRule(new LoggingRule("outer rule")
020 *                             .around(new LoggingRule("middle rule")
021 *                             .around(new LoggingRule("inner rule");
022 *
023 *      &#064;Test
024 *      public void example() {
025 *              assertTrue(true);
026 *     }
027 * }
028 * </pre>
029 *
030 * writes the log
031 *
032 * <pre>
033 * starting outer rule
034 * starting middle rule
035 * starting inner rule
036 * finished inner rule
037 * finished middle rule
038 * finished outer rule
039 * </pre>
040 *
041 * @since 4.10
042 */
043public class RuleChain implements TestRule {
044    private static final RuleChain EMPTY_CHAIN = new RuleChain(
045            Collections.<TestRule>emptyList());
046
047    private List<TestRule> rulesStartingWithInnerMost;
048
049    /**
050     * Returns a {@code RuleChain} without a {@link TestRule}. This method may
051     * be the starting point of a {@code RuleChain}.
052     *
053     * @return a {@code RuleChain} without a {@link TestRule}.
054     */
055    public static RuleChain emptyRuleChain() {
056        return EMPTY_CHAIN;
057    }
058
059    /**
060     * Returns a {@code RuleChain} with a single {@link TestRule}. This method
061     * is the usual starting point of a {@code RuleChain}.
062     *
063     * @param outerRule the outer rule of the {@code RuleChain}.
064     * @return a {@code RuleChain} with a single {@link TestRule}.
065     */
066    public static RuleChain outerRule(TestRule outerRule) {
067        return emptyRuleChain().around(outerRule);
068    }
069
070    private RuleChain(List<TestRule> rules) {
071        this.rulesStartingWithInnerMost = rules;
072    }
073
074    /**
075     * Create a new {@code RuleChain}, which encloses the {@code nextRule} with
076     * the rules of the current {@code RuleChain}.
077     *
078     * @param enclosedRule the rule to enclose.
079     * @return a new {@code RuleChain}.
080     */
081    public RuleChain around(TestRule enclosedRule) {
082        List<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
083        rulesOfNewChain.add(enclosedRule);
084        rulesOfNewChain.addAll(rulesStartingWithInnerMost);
085        return new RuleChain(rulesOfNewChain);
086    }
087
088    /**
089     * {@inheritDoc}
090     */
091    public Statement apply(Statement base, Description description) {
092        for (TestRule each : rulesStartingWithInnerMost) {
093            base = each.apply(base, description);
094        }
095        return base;
096    }
097}