Update of /cvsroot/clirr/clirr/src/java/net/sf/clirr/checks
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14085
Modified Files:
ClassModifierCheck.java
Log Message:
Implemented TODO item: it is not an error to add final to a class if that
class previously had no public or protected constructors.
Index: ClassModifierCheck.java
===================================================================
RCS file: /cvsroot/clirr/clirr/src/java/net/sf/clirr/checks/ClassModifierCheck.java,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- ClassModifierCheck.java 18 Jun 2004 06:52:10 -0000 1.9
+++ ClassModifierCheck.java 21 Jun 2004 07:26:58 -0000 1.10
@@ -23,7 +23,11 @@
import net.sf.clirr.framework.AbstractDiffReporter;
import net.sf.clirr.framework.ApiDiffDispatcher;
import net.sf.clirr.framework.ClassChangeCheck;
+import net.sf.clirr.framework.CheckerException;
+import net.sf.clirr.event.ScopeSelector;
+
import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
/**
* Detects changes in class modifiers (abstract, final).
@@ -46,6 +50,25 @@
/** {@inheritDoc} */
public boolean check(JavaClass compatBaseLine, JavaClass currentVersion)
{
+ final String className = compatBaseLine.getClassName();
+
+ try
+ {
+ ScopeSelector.Scope currentScope = ScopeSelector.getClassScope(currentVersion);
+ if (currentScope == ScopeSelector.SCOPE_PRIVATE)
+ {
+ // for private classes, we don't care if they are now final,
+ // or now abstract, or now an interface.
+ return true;
+ }
+ }
+ catch (CheckerException ex)
+ {
+ log("Unable to determine whether class is private",
+ Severity.ERROR, className, null, null);
+ return true;
+ }
+
final boolean currentIsFinal = currentVersion.isFinal();
final boolean compatIsFinal = compatBaseLine.isFinal();
final boolean currentIsAbstract = currentVersion.isAbstract();
@@ -53,13 +76,6 @@
final boolean currentIsInterface = currentVersion.isInterface();
final boolean compatIsInterface = compatBaseLine.isInterface();
- final String className = compatBaseLine.getClassName();
-
- // TODO: There are cases when nonfinal classes are effectively final
- // because they do not have public or protected ctors. For such
- // classes we should not emit errors when a final modifier is
- // introduced.
-
if (compatIsFinal && !currentIsFinal)
{
log("Removed final modifier in class " + className,
@@ -67,8 +83,17 @@
}
else if (!compatIsFinal && currentIsFinal)
{
- log("Added final modifier in class " + className,
- Severity.ERROR, className, null, null);
+ if (isEffectivelyFinal(compatBaseLine))
+ {
+ log("Added final modifier in class " + className
+ + " (but class was effectively final anyway)",
+ Severity.INFO, className, null, null);
+ }
+ else
+ {
+ log("Added final modifier in class " + className,
+ Severity.ERROR, className, null, null);
+ }
}
// interfaces are always abstract, don't report gender change here
@@ -86,4 +111,36 @@
return true;
}
+ /**
+ * There are cases where nonfinal classes are effectively final
+ * because they do not have public or protected ctors. For such
+ * classes we should not emit errors when a final modifier is
+ * introduced.
+ */
+ private boolean isEffectivelyFinal(JavaClass clazz)
+ {
+ if (clazz.isFinal())
+ {
+ return true;
+ }
+
+ // iterate over all constructors, and detect whether any are
+ // public or protected. If so, return false.
+ Method[] methods = clazz.getMethods();
+ for (int i = 0; i < methods.length; ++i)
+ {
+ Method method = methods[i];
+ final String methodName = method.getName();
+ if (methodName.equals("<init>"))
+ {
+ if (method.isPublic() || method.isProtected())
+ {
+ return false;
+ }
+ }
+ }
+
+ // no public or protected constructor found
+ return true;
+ }
}
|