001: //////////////////////////////////////////////////////////////////////////////
002: // Clirr: compares two versions of a java library for binary compatibility
003: // Copyright (C) 2003 - 2005 Lars Kühne
004: //
005: // This library is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU Lesser General Public
007: // License as published by the Free Software Foundation; either
008: // version 2.1 of the License, or (at your option) any later version.
009: //
010: // This library is distributed in the hope that it will be useful,
011: // but WITHOUT ANY WARRANTY; without even the implied warranty of
012: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: // Lesser General Public License for more details.
014: //
015: // You should have received a copy of the GNU Lesser General Public
016: // License along with this library; if not, write to the Free Software
017: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: //////////////////////////////////////////////////////////////////////////////
019:
020: package net.sf.clirr.core.internal.checks;
021:
022: import java.util.Set;
023: import java.util.TreeSet;
024:
025: import net.sf.clirr.core.Severity;
026: import net.sf.clirr.core.Message;
027: import net.sf.clirr.core.internal.AbstractDiffReporter;
028: import net.sf.clirr.core.internal.ApiDiffDispatcher;
029: import net.sf.clirr.core.internal.ClassChangeCheck;
030: import net.sf.clirr.core.internal.CoIterator;
031: import net.sf.clirr.core.internal.NameComparator;
032: import net.sf.clirr.core.spi.JavaType;
033:
034: /**
035: * Detects changes in the set of interfaces implemented by a class.
036: *
037: * @author lkuehne
038: */
039: public final class InterfaceSetCheck extends AbstractDiffReporter
040: implements ClassChangeCheck {
041: private static final Message MSG_IFACE_ADDED = new Message(4000);
042: private static final Message MSG_IFACE_REMOVED = new Message(4001);
043:
044: /**
045: * Create a new instance of this check.
046: * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners.
047: */
048: public InterfaceSetCheck(ApiDiffDispatcher dispatcher) {
049: super (dispatcher);
050: }
051:
052: /** {@inheritDoc} */
053: public boolean check(JavaType compatBaseline,
054: JavaType currentVersion) {
055: JavaType[] compatInterfaces = compatBaseline.getAllInterfaces();
056: JavaType[] currentInterfaces = currentVersion
057: .getAllInterfaces();
058:
059: // Note: getAllInterfaces might return multiple array entries with the same
060: // interface, so we need to use sets to remove duplicates...
061: Set compat = createClassSet(compatInterfaces);
062: Set current = createClassSet(currentInterfaces);
063:
064: final String className = compatBaseline.getName();
065:
066: CoIterator iter = new CoIterator(new NameComparator(), compat,
067: current);
068:
069: while (iter.hasNext()) {
070: iter.next();
071:
072: JavaType compatInterface = (JavaType) iter.getLeft();
073: JavaType currentInterface = (JavaType) iter.getRight();
074:
075: if (compatInterface != null
076: && className.equals(compatInterface.getName())
077: || currentInterface != null
078: && className.equals(currentInterface.getName())) {
079: // This occurs because an interface has itself in the set of all interfaces.
080: // We can't just let the test below handle this case because that won't
081: // work when a gender change has occurred.
082: continue;
083: }
084:
085: if (compatInterface == null) {
086: // TODO: check whether the class already implements
087: // throwable. If so, this should probably be a warning,
088: // because the presence of this extra interface could
089: // change exception-catching behaviour.
090: //
091: // Actually, it could also change code which uses
092: // "instance-of" and similar methods too, even when not
093: // a throwable. However this is fairly low probability..
094: log(MSG_IFACE_ADDED, Severity.INFO, className, null,
095: null,
096: new String[] { currentInterface.getName() });
097: } else if (currentInterface == null) {
098: log(MSG_IFACE_REMOVED, getSeverity(compatBaseline,
099: Severity.ERROR), className, null, null,
100: new String[] { compatInterface.getName() });
101: }
102: }
103:
104: return true;
105: }
106:
107: /**
108: * Creates a Set of JavaType objects.
109: * @param classes the classes to include in the set, might contain duplicates
110: * @return Set<JavaType>
111: */
112: private Set createClassSet(JavaType[] classes) {
113: // JavaType does not specify the semantics of equals(), so we use a Set implementation
114: // that determines equality by invoking a Comparator instead of calling equals()
115:
116: Set current = new TreeSet(new NameComparator());
117: for (int i = 0; i < classes.length; i++) {
118: current.add(classes[i]);
119: }
120: return current;
121: }
122: }
|