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 net.sf.clirr.core.Severity;
023: import net.sf.clirr.core.ScopeSelector;
024: import net.sf.clirr.core.Message;
025: import net.sf.clirr.core.internal.AbstractDiffReporter;
026: import net.sf.clirr.core.internal.ApiDiffDispatcher;
027: import net.sf.clirr.core.internal.ClassChangeCheck;
028: import net.sf.clirr.core.spi.JavaType;
029: import net.sf.clirr.core.spi.Scope;
030:
031: /**
032: * Detects changes in class access declaration, for both "top-level" classes,
033: * and nested classes.
034: * <p>
035: * Java class files only ever contain scope specifiers of "public" or "package".
036: * For top-level classes, this is expected: it is not possible to have a
037: * top-level protected or private class.
038: * <p>
039: * However nested classes <i>can</i> be declared as protected or private. The
040: * way to tell the real scope of a nested class is to ignore the scope in
041: * the actual class file itself, and instead look in the "InnerClasses"
042: * attribute stored on the enclosing class. This is exactly what the java
043: * compiler does when compiling, and what the jvm does when verifying class
044: * linkage at runtime.
045: *
046: * @author Simon Kitching
047: */
048: public final class ClassScopeCheck extends AbstractDiffReporter
049: implements ClassChangeCheck {
050: private static final Message MSG_SCOPE_INCREASED = new Message(1000);
051: private static final Message MSG_SCOPE_DECREASED = new Message(1001);
052: private static final Message MSG_ERROR_DETERMINING_SCOPE_OLD = new Message(
053: 1002);
054: private static final Message MSG_ERROR_DETERMINING_SCOPE_NEW = new Message(
055: 1003);
056:
057: private ScopeSelector scopeSelector;
058:
059: /**
060: * Create a new instance of this check.
061: * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners.
062: */
063: public ClassScopeCheck(ApiDiffDispatcher dispatcher,
064: ScopeSelector scopeSelector) {
065: super (dispatcher);
066: this .scopeSelector = scopeSelector;
067: }
068:
069: /** {@inheritDoc} */
070: public boolean check(JavaType compatBaseline,
071: JavaType currentVersion) {
072: Scope bScope = compatBaseline.getEffectiveScope();
073: Scope cScope = currentVersion.getEffectiveScope();
074:
075: if (!scopeSelector.isSelected(bScope)
076: && !scopeSelector.isSelected(cScope)) {
077: // neither the old nor the new class are "visible" at the scope
078: // the user of this class cares about, so just skip this test
079: // and all following tests for this pair of classes.
080: return false;
081: }
082:
083: if (cScope.isMoreVisibleThan(bScope)) {
084: String[] args = { bScope.getDesc(), cScope.getDesc() };
085:
086: log(MSG_SCOPE_INCREASED, Severity.INFO, compatBaseline
087: .getName(), null, null, args);
088: } else if (cScope.isLessVisibleThan(bScope)) {
089: String[] args = { bScope.getDesc(), cScope.getDesc() };
090:
091: log(MSG_SCOPE_DECREASED, getSeverity(compatBaseline,
092: Severity.ERROR), compatBaseline.getName(), null,
093: null, args);
094: }
095:
096: // Apply further checks only if both versions of the class have scopes
097: // of interest. For example, when the user is only interested in
098: // public & protected classes, then for classes which have just become
099: // public/protected we just want to report that it is now "visible";
100: // because the class was not visible before the differences since its
101: // last version are not relevant. And for classes which are no longer
102: // public/protected, we just want to report that the whole class is no
103: // longer "visible"; as it is not visible to users any changes to it
104: // are irrelevant.
105: return scopeSelector.isSelected(bScope)
106: && scopeSelector.isSelected(cScope);
107: }
108:
109: }
|