1 package org.junit.runners;
2 
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.IdentityHashMap;
7 import java.util.List;
8 
9 import org.junit.Rule;
10 import org.junit.rules.MethodRule;
11 import org.junit.rules.TestRule;
12 import org.junit.runner.Description;
13 import org.junit.runners.model.FrameworkMethod;
14 import org.junit.runners.model.Statement;
15 
16 /**
17  * Data structure for ordering of {@link TestRule}/{@link MethodRule} instances.
18  *
19  * @since 4.13
20  */
21 class RuleContainer {
22     private final IdentityHashMap<Object, Integer> orderValues = new IdentityHashMap<Object, Integer>();
23     private final List<TestRule> testRules = new ArrayList<TestRule>();
24     private final List<MethodRule> methodRules = new ArrayList<MethodRule>();
25 
26     /**
27      * Sets order value for the specified rule.
28      */
setOrder(Object rule, int order)29     public void setOrder(Object rule, int order) {
30         orderValues.put(rule, order);
31     }
32 
add(MethodRule methodRule)33     public void add(MethodRule methodRule) {
34         methodRules.add(methodRule);
35     }
36 
add(TestRule testRule)37     public void add(TestRule testRule) {
38         testRules.add(testRule);
39     }
40 
41     static final Comparator<RuleEntry> ENTRY_COMPARATOR = new Comparator<RuleEntry>() {
42         public int compare(RuleEntry o1, RuleEntry o2) {
43             int result = compareInt(o1.order, o2.order);
44             return result != 0 ? result : o1.type - o2.type;
45         }
46 
47         private int compareInt(int a, int b) {
48             return (a < b) ? 1 : (a == b ? 0 : -1);
49         }
50     };
51 
52     /**
53      * Returns entries in the order how they should be applied, i.e. inner-to-outer.
54      */
getSortedEntries()55     private List<RuleEntry> getSortedEntries() {
56         List<RuleEntry> ruleEntries = new ArrayList<RuleEntry>(
57                 methodRules.size() + testRules.size());
58         for (MethodRule rule : methodRules) {
59             ruleEntries.add(new RuleEntry(rule, RuleEntry.TYPE_METHOD_RULE, orderValues.get(rule)));
60         }
61         for (TestRule rule : testRules) {
62             ruleEntries.add(new RuleEntry(rule, RuleEntry.TYPE_TEST_RULE, orderValues.get(rule)));
63         }
64         Collections.sort(ruleEntries, ENTRY_COMPARATOR);
65         return ruleEntries;
66     }
67 
68     /**
69      * Applies all the rules ordered accordingly to the specified {@code statement}.
70      */
apply(FrameworkMethod method, Description description, Object target, Statement statement)71     public Statement apply(FrameworkMethod method, Description description, Object target,
72             Statement statement) {
73         if (methodRules.isEmpty() && testRules.isEmpty()) {
74             return statement;
75         }
76         Statement result = statement;
77         for (RuleEntry ruleEntry : getSortedEntries()) {
78             if (ruleEntry.type == RuleEntry.TYPE_TEST_RULE) {
79                 result = ((TestRule) ruleEntry.rule).apply(result, description);
80             } else {
81                 result = ((MethodRule) ruleEntry.rule).apply(result, method, target);
82             }
83         }
84         return result;
85     }
86 
87     /**
88      * Returns rule instances in the order how they should be applied, i.e. inner-to-outer.
89      * VisibleForTesting
90      */
getSortedRules()91     List<Object> getSortedRules() {
92         List<Object> result = new ArrayList<Object>();
93         for (RuleEntry entry : getSortedEntries()) {
94             result.add(entry.rule);
95         }
96         return result;
97     }
98 
99     static class RuleEntry {
100         static final int TYPE_TEST_RULE = 1;
101         static final int TYPE_METHOD_RULE = 0;
102 
103         final Object rule;
104         final int type;
105         final int order;
106 
RuleEntry(Object rule, int type, Integer order)107         RuleEntry(Object rule, int type, Integer order) {
108             this.rule = rule;
109             this.type = type;
110             this.order = order != null ? order.intValue() : Rule.DEFAULT_ORDER;
111         }
112     }
113 }
114