View Javadoc

1   //////////////////////////////////////////////////////////////////////////////
2   // Clirr: compares two versions of a java library for binary compatibility
3   // Copyright (C) 2003 - 2005  Lars Kühne
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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              // neither the old nor the new class are "visible" at the scope
78              // the user of this class cares about, so just skip this test
79              // and all following tests for this pair of classes.
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          // Apply further checks only if both versions of the class have scopes
100         // of interest. For example, when the user is only interested in
101         // public & protected classes, then for classes which have just become
102         // public/protected we just want to report that it is now "visible";
103         // because the class was not visible before the differences since its
104         // last version are not relevant. And for classes which are no longer
105         // public/protected, we just want to report that the whole class is no
106         // longer "visible"; as it is not visible to users any changes to it
107         // are irrelevant.
108         return scopeSelector.isSelected(bScope) && scopeSelector.isSelected(cScope);
109     }
110 
111 }