1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package net.sf.clirr.core.internal.checks;
21
22 import net.sf.clirr.core.Severity;
23 import net.sf.clirr.core.ScopeSelector;
24 import net.sf.clirr.core.Message;
25 import net.sf.clirr.core.internal.AbstractDiffReporter;
26 import net.sf.clirr.core.internal.ApiDiffDispatcher;
27 import net.sf.clirr.core.internal.ClassChangeCheck;
28 import net.sf.clirr.core.spi.JavaType;
29 import net.sf.clirr.core.spi.Scope;
30
31 /***
32 * Detects changes in class access declaration, for both "top-level" classes,
33 * and nested classes.
34 * <p>
35 * Java class files only ever contain scope specifiers of "public" or "package".
36 * For top-level classes, this is expected: it is not possible to have a
37 * top-level protected or private class.
38 * <p>
39 * However nested classes <i>can</i> be declared as protected or private. The
40 * way to tell the real scope of a nested class is to ignore the scope in
41 * the actual class file itself, and instead look in the "InnerClasses"
42 * attribute stored on the enclosing class. This is exactly what the java
43 * compiler does when compiling, and what the jvm does when verifying class
44 * linkage at runtime.
45 *
46 * @author Simon Kitching
47 */
48 public final class ClassScopeCheck
49 extends AbstractDiffReporter
50 implements ClassChangeCheck
51 {
52 private static final Message MSG_SCOPE_INCREASED = new Message(1000);
53 private static final Message MSG_SCOPE_DECREASED = new Message(1001);
54 private static final Message MSG_ERROR_DETERMINING_SCOPE_OLD = new Message(1002);
55 private static final Message MSG_ERROR_DETERMINING_SCOPE_NEW = new Message(1003);
56
57 private ScopeSelector scopeSelector;
58
59 /***
60 * Create a new instance of this check.
61 * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners.
62 */
63 public ClassScopeCheck(ApiDiffDispatcher dispatcher, ScopeSelector scopeSelector)
64 {
65 super(dispatcher);
66 this.scopeSelector = scopeSelector;
67 }
68
69 /*** {@inheritDoc} */
70 public boolean check(JavaType compatBaseline, JavaType currentVersion)
71 {
72 Scope bScope = compatBaseline.getEffectiveScope();
73 Scope cScope = currentVersion.getEffectiveScope();
74
75 if (!scopeSelector.isSelected(bScope) && !scopeSelector.isSelected(cScope))
76 {
77
78
79
80 return false;
81 }
82
83 if (cScope.isMoreVisibleThan(bScope))
84 {
85 String[] args = {bScope.getDesc(), cScope.getDesc()};
86
87 log(MSG_SCOPE_INCREASED,
88 Severity.INFO, compatBaseline.getName(), null, null, args);
89 }
90 else if (cScope.isLessVisibleThan(bScope))
91 {
92 String[] args = {bScope.getDesc(), cScope.getDesc()};
93
94 log(MSG_SCOPE_DECREASED,
95 getSeverity(compatBaseline, Severity.ERROR),
96 compatBaseline.getName(), null, null, args);
97 }
98
99
100
101
102
103
104
105
106
107
108 return scopeSelector.isSelected(bScope) && scopeSelector.isSelected(cScope);
109 }
110
111 }