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;
21  
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import net.sf.clirr.core.internal.ApiDiffDispatcher;
27  import net.sf.clirr.core.internal.ClassChangeCheck;
28  import net.sf.clirr.core.internal.CoIterator;
29  import net.sf.clirr.core.internal.NameComparator;
30  import net.sf.clirr.core.internal.checks.ClassHierarchyCheck;
31  import net.sf.clirr.core.internal.checks.ClassModifierCheck;
32  import net.sf.clirr.core.internal.checks.ClassScopeCheck;
33  import net.sf.clirr.core.internal.checks.FieldSetCheck;
34  import net.sf.clirr.core.internal.checks.GenderChangeCheck;
35  import net.sf.clirr.core.internal.checks.InterfaceSetCheck;
36  import net.sf.clirr.core.internal.checks.MethodSetCheck;
37  import net.sf.clirr.core.spi.JavaType;
38  import net.sf.clirr.core.spi.Scope;
39  
40  /***
41   * This is the main class to be used by Clirr frontends,
42   * it implements the checking functionality of Clirr.
43   * Frontends can create an instance of this class
44   * and register themselves as DiffListeners, they are then
45   * informed whenever an API change is detected by the
46   * reportDiffs method.
47   *
48   * @author lkuehne
49   */
50  public final class Checker implements ApiDiffDispatcher
51  {
52      private static final Message MSG_CLASS_ADDED = new Message(8000);
53      private static final Message MSG_CLASS_REMOVED = new Message(8001);
54  
55      private List listeners = new ArrayList();
56  
57      private List classChecks = new ArrayList();
58  
59      private ScopeSelector scopeSelector = new ScopeSelector();
60  
61      /***
62       * Package visible constructor for unit testing.
63       */
64      Checker(ClassChangeCheck ccc)
65      {
66          if (ccc != null)
67          {
68              classChecks.add(ccc);
69          }
70      }
71  
72      /***
73       * Creates a new Checker.
74       */
75      public Checker()
76      {
77          classChecks.add(new ClassScopeCheck(this, scopeSelector));
78          classChecks.add(new GenderChangeCheck(this));
79          classChecks.add(new ClassModifierCheck(this));
80          classChecks.add(new InterfaceSetCheck(this));
81          classChecks.add(new ClassHierarchyCheck(this));
82          classChecks.add(new FieldSetCheck(this, scopeSelector));
83          classChecks.add(new MethodSetCheck(this, scopeSelector));
84      }
85  
86      public ScopeSelector getScopeSelector()
87      {
88          return scopeSelector;
89      }
90  
91      public void addDiffListener(DiffListener listener)
92      {
93          listeners.add(listener);
94      }
95  
96      private void fireStart()
97      {
98          for (Iterator it = listeners.iterator(); it.hasNext();)
99          {
100             DiffListener diffListener = (DiffListener) it.next();
101             diffListener.start();
102         }
103     }
104 
105     private void fireStop()
106     {
107         for (Iterator it = listeners.iterator(); it.hasNext();)
108         {
109             DiffListener diffListener = (DiffListener) it.next();
110             diffListener.stop();
111         }
112     }
113 
114     public void fireDiff(ApiDifference diff)
115     {
116         for (Iterator it = listeners.iterator(); it.hasNext();)
117         {
118             DiffListener diffListener = (DiffListener) it.next();
119             diffListener.reportDiff(diff);
120         }
121     }
122 
123     /***
124      * Checks two sets of classes for api changes and reports
125      * them to the DiffListeners.
126      * @param compatibilityBaseline the classes that form the
127      *        compatibility baseline to check against
128      * @param currentVersion the classes that are checked for
129      *        compatibility with compatibilityBaseline
130      */
131     public void reportDiffs(
132         JavaType[] compatibilityBaseline, JavaType[] currentVersion)
133         throws CheckerException
134     {
135         fireStart();
136         runClassChecks(compatibilityBaseline, currentVersion);
137         fireStop();
138     }
139 
140     private void runClassChecks(
141         JavaType[] compat, JavaType[] current)
142         throws CheckerException
143     {
144         CoIterator iter = new CoIterator(
145             new NameComparator(), compat, current);
146 
147         while (iter.hasNext())
148         {
149             iter.next();
150 
151             JavaType compatBaselineClass = (JavaType) iter.getLeft();
152             JavaType currentClass = (JavaType) iter.getRight();
153 
154             if (compatBaselineClass == null)
155             {
156                 if (!scopeSelector.isSelected(currentClass.getEffectiveScope()))
157                 {
158                     continue;   
159                 }
160                 final String className = currentClass.getName();
161                 final ApiDifference diff =
162                     new ApiDifference(
163                         MSG_CLASS_ADDED, Severity.INFO, className,
164                         null, null, null);
165                 fireDiff(diff);
166             }
167             else if (currentClass == null)
168             {
169                 final Scope classScope = compatBaselineClass.getEffectiveScope();
170                 if (!scopeSelector.isSelected(classScope))
171                 {
172                     continue;   
173                 }
174                 final String className = compatBaselineClass.getName();
175                 final Severity severity = classScope.isLessVisibleThan(
176                         Scope.PROTECTED) ? Severity.INFO : Severity.ERROR;
177                 final ApiDifference diff =
178                     new ApiDifference(
179                         MSG_CLASS_REMOVED, severity, className,
180                         null, null, null);
181                 fireDiff(diff);
182             }
183             else
184             {
185                 // class is available in both releases
186                 boolean continueTesting = true;
187                 for (Iterator it = classChecks.iterator(); it.hasNext() && continueTesting;)
188                 {
189                     ClassChangeCheck classChangeCheck = (ClassChangeCheck) it.next();
190                     continueTesting = classChangeCheck.check(
191                         compatBaselineClass, currentClass);
192                 }
193             }
194         }
195     }
196 }