1package org.junit.rules;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6
7import org.junit.runner.Description;
8import org.junit.runners.model.Statement;
9
10/**
11 * The RuleChain rule allows ordering of TestRules. You create a
12 * {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
13 * {@link #around(TestRule)}:
14 *
15 * <pre>
16 * public static class UseRuleChain {
17 * 	&#064;Rule
18 * 	public RuleChain chain= RuleChain
19 * 	                       .outerRule(new LoggingRule("outer rule")
20 * 	                       .around(new LoggingRule("middle rule")
21 * 	                       .around(new LoggingRule("inner rule");
22 *
23 * 	&#064;Test
24 * 	public void example() {
25 * 		assertTrue(true);
26 *     }
27 * }
28 * </pre>
29 *
30 * writes the log
31 *
32 * <pre>
33 * starting outer rule
34 * starting middle rule
35 * starting inner rule
36 * finished inner rule
37 * finished middle rule
38 * finished outer rule
39 * </pre>
40 *
41 * @since 4.10
42 */
43public class RuleChain implements TestRule {
44    private static final RuleChain EMPTY_CHAIN = new RuleChain(
45            Collections.<TestRule>emptyList());
46
47    private List<TestRule> rulesStartingWithInnerMost;
48
49    /**
50     * Returns a {@code RuleChain} without a {@link TestRule}. This method may
51     * be the starting point of a {@code RuleChain}.
52     *
53     * @return a {@code RuleChain} without a {@link TestRule}.
54     */
55    public static RuleChain emptyRuleChain() {
56        return EMPTY_CHAIN;
57    }
58
59    /**
60     * Returns a {@code RuleChain} with a single {@link TestRule}. This method
61     * is the usual starting point of a {@code RuleChain}.
62     *
63     * @param outerRule the outer rule of the {@code RuleChain}.
64     * @return a {@code RuleChain} with a single {@link TestRule}.
65     */
66    public static RuleChain outerRule(TestRule outerRule) {
67        return emptyRuleChain().around(outerRule);
68    }
69
70    private RuleChain(List<TestRule> rules) {
71        this.rulesStartingWithInnerMost = rules;
72    }
73
74    /**
75     * Create a new {@code RuleChain}, which encloses the {@code nextRule} with
76     * the rules of the current {@code RuleChain}.
77     *
78     * @param enclosedRule the rule to enclose.
79     * @return a new {@code RuleChain}.
80     */
81    public RuleChain around(TestRule enclosedRule) {
82        List<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
83        rulesOfNewChain.add(enclosedRule);
84        rulesOfNewChain.addAll(rulesStartingWithInnerMost);
85        return new RuleChain(rulesOfNewChain);
86    }
87
88    /**
89     * {@inheritDoc}
90     */
91    public Statement apply(Statement base, Description description) {
92        for (TestRule each : rulesStartingWithInnerMost) {
93            base = each.apply(base, description);
94        }
95        return base;
96    }
97}