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 java.util.Set;
23  import java.util.TreeSet;
24  
25  import net.sf.clirr.core.Severity;
26  import net.sf.clirr.core.Message;
27  import net.sf.clirr.core.internal.AbstractDiffReporter;
28  import net.sf.clirr.core.internal.ApiDiffDispatcher;
29  import net.sf.clirr.core.internal.ClassChangeCheck;
30  import net.sf.clirr.core.internal.CoIterator;
31  import net.sf.clirr.core.internal.NameComparator;
32  import net.sf.clirr.core.spi.JavaType;
33  
34  /***
35   * Detects changes in the set of interfaces implemented by a class.
36   *
37   * @author lkuehne
38   */
39  public final class InterfaceSetCheck
40      extends AbstractDiffReporter
41      implements ClassChangeCheck
42  {
43      private static final Message MSG_IFACE_ADDED = new Message(4000);
44      private static final Message MSG_IFACE_REMOVED = new Message(4001);
45  
46      /***
47       * Create a new instance of this check.
48       * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners.
49       */
50      public InterfaceSetCheck(ApiDiffDispatcher dispatcher)
51      {
52          super(dispatcher);
53      }
54  
55      /*** {@inheritDoc} */
56      public boolean check(JavaType compatBaseline, JavaType currentVersion)
57      {
58          JavaType[] compatInterfaces = compatBaseline.getAllInterfaces();
59          JavaType[] currentInterfaces = currentVersion.getAllInterfaces();
60  
61          // Note: getAllInterfaces might return multiple array entries with the same
62          // interface, so we need to use sets to remove duplicates...
63          Set compat = createClassSet(compatInterfaces);
64          Set current = createClassSet(currentInterfaces);
65  
66          final String className = compatBaseline.getName();
67  
68          CoIterator iter = new CoIterator(
69              new NameComparator(), compat, current);
70  
71          while (iter.hasNext())
72          {
73              iter.next();
74  
75              JavaType compatInterface = (JavaType) iter.getLeft();
76              JavaType currentInterface = (JavaType) iter.getRight();
77  
78              if (compatInterface != null && className.equals(compatInterface.getName())
79                  || currentInterface != null && className.equals(currentInterface.getName()))
80              {
81                  // This occurs because an interface has itself in the set of all interfaces.
82                  // We can't just let the test below handle this case because that won't
83                  // work when a gender change has occurred.
84                  continue;
85              }
86  
87              if (compatInterface == null)
88              {
89                  // TODO: check whether the class already implements
90                  // throwable. If so, this should probably be a warning,
91                  // because the presence of this extra interface could
92                  // change exception-catching behaviour.
93                  //
94                  // Actually, it could also change code which uses
95                  // "instance-of" and similar methods too, even when not
96                  // a throwable. However this is fairly low probability..
97                  log(MSG_IFACE_ADDED,
98                          Severity.INFO, className, null, null,
99                          new String[] {currentInterface.getName()});
100             }
101             else if (currentInterface == null)
102             {
103                 log(MSG_IFACE_REMOVED,
104                         getSeverity(compatBaseline, Severity.ERROR),
105                         className, null, null,
106                         new String[] {compatInterface.getName()});
107             }
108         }
109 
110         return true;
111     }
112 
113     /***
114      * Creates a Set of JavaType objects.
115      * @param classes the classes to include in the set, might contain duplicates
116      * @return Set<JavaType>
117      */
118     private Set createClassSet(JavaType[] classes)
119     {
120         // JavaType does not specify the semantics of equals(), so we use a Set implementation
121         // that determines equality by invoking a Comparator instead of calling equals()
122 
123         Set current = new TreeSet(new NameComparator());
124         for (int i = 0; i < classes.length; i++)
125         {
126             current.add(classes[i]);
127         }
128         return current;
129     }
130 }