Major refactorings to allow the use of SeverityLevels in FileSetChecks

as well as in Checks and to simplify logging of messages in FileSetChecks.

Introduction of a new class called AbstractViolationReporter, from which
both Check and FileSetCheck are derived.

Severity is passed to submodules by means of the Contextualizable
mechanism. This means that the user can f.ex. set severity to INFO
in a TreeWalker and all Checks below that TreeWalker will use severity INFO.
This commit is contained in:
Lars Kühne 2003-03-26 19:52:30 +00:00
parent d4cee63283
commit d216ec7525
10 changed files with 348 additions and 220 deletions

View File

@ -508,23 +508,26 @@
{
if (files != null && files.length > max) {
// Build the error list. Here we fire only one error
LocalizedMessage[] errors = new LocalizedMessage[1];
// get the resource bundle to use for the message
// will return "com.mycompany.checks.messages"
final String bundle = getMessageBundle();
// create the message arguments
Object[] msgArgs = new Object[]{new Integer(max)};
// create the actual message
errors[0] = new LocalizedMessage(
0, bundle, "max.files.exceeded", msgArgs);
// fire the errors to the AuditListeners
// figure out the file that contains the error
final String path = files[max].getPath();
getMessageDispatcher().fireErrors(path, errors);
// message dispatcher is used to fire AuditEvents
MessageDispatcher dispatcher = getMessageDispatcher();
// signal start of file to AuditListeners
dispatcher.fireFileStarted(path);
// log the message
log(0, "max.files.exceeded", new Integer(max));
// you can call log() multiple times to flag multiple
// errors in the same file
// fire the errors for this file to the AuditListeners
dispatcher.fireErrors(path, getMessageCollector.getMessages());
// signal end of file to AuditListeners
dispatcher.fireFileFinished(path);
}
}
}

View File

@ -120,6 +120,18 @@ public class Checker extends AutomaticBean
/** the context of all child components */
private Context mChildContext;
/**
* The severity level of any violations found by submodules.
* The value of this property is passed to submodules via
* contextualize().
*
* Note: Since the Checker is merely a container for modules
* it does not make sense to implement logging functionality
* here. Consequently Checker does not extend AbstractViolationReporter,
* leading to a bit of duplicated code for severity level setting.
*/
private SeverityLevel mSeverityLevel = SeverityLevel.ERROR;
/**
* Creates a new <code>Checker</code> instance.
* The instance needs to be contextualized and configured.
@ -147,6 +159,7 @@ public class Checker extends AutomaticBean
final DefaultContext context = new DefaultContext();
context.add("classLoader", mLoader);
context.add("moduleFactory", mModuleFactory);
context.add("severity", mSeverityLevel.getName());
mChildContext = context;
}
@ -342,4 +355,17 @@ public class Checker extends AutomaticBean
{
mLocaleLanguage = aLocaleLanguage;
}
/**
* Sets the severity level. The string should be one of the names
* defined in the <code>SeverityLevel</code> class.
*
* @param aSeverity The new severity level
* @see SeverityLevel
*/
public final void setSeverity(String aSeverity)
{
mSeverityLevel = SeverityLevel.getInstance(aSeverity);
}
}

View File

@ -41,7 +41,6 @@ import com.puppycrawl.tools.checkstyle.api.Context;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
import com.puppycrawl.tools.checkstyle.api.LocalizedMessages;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.api.Utils;
@ -105,8 +104,6 @@ public final class TreeWalker
private final Map mTokenToChecks = new HashMap();
/** all the registered checks */
private final Set mAllChecks = new HashSet();
/** collects the error messages */
private final LocalizedMessages mMessages;
/** the distance between tab stops */
private int mTabWidth = 8;
/** cache file **/
@ -133,7 +130,6 @@ public final class TreeWalker
public TreeWalker()
{
setFileExtensions(new String[]{"java"});
mMessages = new LocalizedMessages();
// TODO: I (lkuehne) can't believe I wrote this! HACK HACK HACK!
@ -184,7 +180,8 @@ public final class TreeWalker
{
DefaultContext checkContext = new DefaultContext();
checkContext.add("classLoader", mClassLoader);
checkContext.add("messages", mMessages);
checkContext.add("messages", getMessageCollector());
checkContext.add("severity", getSeverity());
// TODO: hmmm.. this looks less than elegant
// we have just parsed the string,
// now we're recreating it only to parse it again a few moments later
@ -229,7 +226,7 @@ public final class TreeWalker
return;
}
mMessages.reset();
getMessageCollector().reset();
try {
getMessageDispatcher().fireFileStarted(fileName);
final String[] lines = Utils.getLines(fileName);
@ -238,37 +235,41 @@ public final class TreeWalker
walk(rootAST, contents);
}
catch (FileNotFoundException fnfe) {
mMessages.add(new LocalizedMessage(0, Defn.CHECKSTYLE_BUNDLE,
"general.fileNotFound", null));
getMessageCollector().add(new LocalizedMessage(
0, Defn.CHECKSTYLE_BUNDLE,
"general.fileNotFound", null));
}
catch (IOException ioe) {
mMessages.add(new LocalizedMessage(
0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {ioe.getMessage()}));
getMessageCollector().add(new LocalizedMessage(
0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {ioe.getMessage()}));
}
catch (RecognitionException re) {
mMessages.add(new LocalizedMessage(0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {re.getMessage()}));
getMessageCollector().add(new LocalizedMessage(
0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {re.getMessage()}));
}
catch (TokenStreamException te) {
mMessages.add(new LocalizedMessage(0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {te.getMessage()}));
getMessageCollector().add(new LocalizedMessage(
0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {te.getMessage()}));
}
catch (Throwable err) {
mMessages.add(new LocalizedMessage(0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {"" + err}));
getMessageCollector().add(new LocalizedMessage(
0, Defn.CHECKSTYLE_BUNDLE,
"general.exception",
new String[] {"" + err}));
}
if (mMessages.size() == 0) {
if (getMessageCollector().size() == 0) {
mCache.checkedOk(fileName, timestamp);
}
else {
getMessageDispatcher().fireErrors(
fileName, mMessages.getMessages());
fileName, getMessageCollector().getMessages());
}
getMessageDispatcher().fireFileFinished(fileName);
@ -348,7 +349,7 @@ public final class TreeWalker
*/
private void walk(DetailAST aAST, FileContents aContents)
{
mMessages.reset();
getMessageCollector().reset();
notifyBegin(aAST, aContents);
// empty files are not flagged by javac, will yield aAST == null
@ -539,4 +540,5 @@ public final class TreeWalker
mCache.destroy();
super.destroy();
}
}

View File

@ -28,7 +28,7 @@ import java.util.ArrayList;
* @author lkuehne
*/
public abstract class AbstractFileSetCheck
extends AutomaticBean implements FileSetCheck
extends AbstractViolationReporter implements FileSetCheck
{
/** The dispatcher errors are fired to. */
private MessageDispatcher mDispatcher = null;
@ -36,6 +36,17 @@ public abstract class AbstractFileSetCheck
/** the file extensions that are accepted by this filter */
private String[] mFileExtensions = {};
/** collects the error messages */
private final LocalizedMessages mMessages;
/**
* initializes the AbstractFileSetCheck properties.
*/
protected AbstractFileSetCheck()
{
mMessages = new LocalizedMessages();
}
/**
* Does nothing.
* @see com.puppycrawl.tools.checkstyle.api.FileSetCheck
@ -50,7 +61,12 @@ public abstract class AbstractFileSetCheck
mDispatcher = aDispatcher;
}
/** @return the current MessageDispatcher. */
/**
* A message dispatcher is used to fire violation messages to
* interested audit listeners.
*
* @return the current MessageDispatcher.
*/
protected final MessageDispatcher getMessageDispatcher()
{
return mDispatcher;
@ -114,17 +130,39 @@ public abstract class AbstractFileSetCheck
}
/**
* Returns the Message bundle name to use for this FileSetCheck.
* The default implementation uses the <code>messages</code> bundle
* in the same package as this FileSetCeck.
* @return the message bundle name
* Returns the collector for violation messages.
* Subclasses can use the collector to find out the violation
* messages to fire via the message dispatcher.
*
* @return the collector for localized messages.
*/
protected String getMessageBundle()
protected final LocalizedMessages getMessageCollector()
{
final String className = getClass().getName();
final int pkgEndIndex = className.lastIndexOf('.');
final String pkgName = className.substring(0, pkgEndIndex);
final String bundle = pkgName + ".messages";
return bundle;
return mMessages;
}
/**
* Adds a violation message to the
* {@link #getMessageCollector message collector}.
* @see AbstractViolationReporter#log(int, String, Object[])
*/
protected final void log(int aLine, String aKey, Object aArgs[])
{
log(aLine, 0, aKey, aArgs);
}
/**
* Adds a violation message to the
* {@link #getMessageCollector message collector}.
* @see AbstractViolationReporter#log(int, int, String, Object[])
*/
protected final void log(int aLineNo, int aColNo,
String aKey, Object[] aArgs)
{
getMessageCollector().add(new LocalizedMessage(
aLineNo, aColNo, getMessageBundle(),
aKey, aArgs, getSeverityLevel()));
}
}

View File

@ -0,0 +1,211 @@
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2002 Oliver Burn
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.api;
/**
* Serves as an abstract base class for all modules that report inspection
* findings. Such modules have a Severity level which is used for the
* {@link LocalizedMessage localized messages} that are created by the module.
*
* @author lkuehne
*/
public abstract class AbstractViolationReporter extends AutomaticBean
{
/** resuable constant for message formating */
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
/** the severity level of any violations found */
private SeverityLevel mSeverityLevel = SeverityLevel.ERROR;
/**
* Returns the severity level of the messages generated by this module.
* @return the severity level
* @see SeverityLevel
* @see LocalizedMessage#getSeverityLevel
*/
public final SeverityLevel getSeverityLevel()
{
return mSeverityLevel;
}
/**
* Sets the severity level. The string should be one of the names
* defined in the <code>SeverityLevel</code> class.
*
* @param aSeverity The new severity level
* @see SeverityLevel
*/
public final void setSeverity(String aSeverity)
{
mSeverityLevel = SeverityLevel.getInstance(aSeverity);
}
/**
* Get the severity level's name.
*
* @return the check's severity level name.
*/
public final String getSeverity()
{
return mSeverityLevel.getName();
}
/**
* Log a message.
*
* @param aLine the line number where the error was found
* @param aKey the message that describes the error
*/
protected final void log(int aLine, String aKey)
{
log(aLine, aKey, EMPTY_OBJECT_ARRAY);
}
/**
* Helper method to log a LocalizedMessage. Column defaults to 0.
*
* @param aLineNo line number to associate with the message
* @param aKey key to locale message format
* @param aArg0 first argument
*/
protected final void log(int aLineNo, String aKey, Object aArg0)
{
log(aLineNo, aKey, new Object[] {aArg0});
}
/**
* Helper method to log a LocalizedMessage. Column defaults to 0.
*
* @param aLineNo line number to associate with the message
* @param aKey key to locale message format
* @param aArg0 first argument
* @param aArg1 second argument
*/
protected final void log(int aLineNo, String aKey,
Object aArg0, Object aArg1)
{
log(aLineNo, aKey, new Object[] {aArg0, aArg1});
}
/**
* Helper method to log a LocalizedMessage. Column defaults to 0.
*
* @param aLineNo line number to associate with the message
* @param aKey key to locale message format
* @param aArg0 first argument
* @param aArg1 second argument
* @param aArg2 third argument
*/
protected final void log(int aLineNo, String aKey,
Object aArg0, Object aArg1, Object aArg2)
{
log(aLineNo, aKey, new Object[] {aArg0, aArg1, aArg2});
}
/**
* Helper method to log a LocalizedMessage.
*
* @param aLineNo line number to associate with the message
* @param aColNo column number to associate with the message
* @param aKey key to locale message format
*/
protected final void log(int aLineNo, int aColNo, String aKey)
{
log(aLineNo, aColNo, aKey, EMPTY_OBJECT_ARRAY);
}
/**
* Helper method to log a LocalizedMessage.
*
* @param aLineNo line number to associate with the message
* @param aColNo column number to associate with the message
* @param aKey key to locale message format
* @param aArg0 an <code>Object</code> value
*/
protected final void log(int aLineNo, int aColNo, String aKey,
Object aArg0)
{
log(aLineNo, aColNo, aKey, new Object[] {aArg0});
}
/**
* Helper method to log a LocalizedMessage.
*
* @param aLineNo line number to associate with the message
* @param aColNo column number to associate with the message
* @param aKey key to locale message format
* @param aArg0 an <code>Object</code> value
* @param aArg1 an <code>Object</code> value
*/
protected final void log(int aLineNo, int aColNo, String aKey,
Object aArg0, Object aArg1)
{
log(aLineNo, aColNo, aKey, new Object[] {aArg0, aArg1});
}
/**
* Returns the message bundle name resourcebundle that contains the messages
* used by this module.
* <p>
* The default implementation expects the resource files to be named
* messages.properties, messages_de.properties, etc. The file must
* be placed in the same package as the module implementation.
* </p>
* <p>
* Example: If you write com/foo/MyCoolCheck, create resource files
* com/foo/messages.properties, com/foo/messages_de.properties, etc.
* </p>
*
* @return name of a resource bundle that contains the messages
* used by this module.
*/
protected String getMessageBundle()
{
final String className = this.getClass().getName();
final int endIndex = className.lastIndexOf('.');
final String packageName = className.substring(0, endIndex);
return packageName + "." + "messages";
}
/**
* Log a message that has no column information.
*
* @param aLine the line number where the error was found
* @param aKey the message that describes the error
* @param aArgs the details of the message
*
* @see java.text.MessageFormat
*/
protected abstract void log(int aLine, String aKey, Object aArgs[]);
/**
* Log a message that has column information.
*
* @param aLine the line number where the error was found
* @param aCol the column number where the error was found
* @param aKey the message that describes the error
* @param aArgs the details of the message
*
* @see java.text.MessageFormat
*/
protected abstract void log(int aLine, int aCol,
String aKey, Object[] aArgs);
}

View File

@ -30,10 +30,8 @@ import java.util.Set;
* @see <a href="./{@docRoot}/../writingchecks.html" target="_top">Writing
* your own checks</a>
*/
public abstract class Check extends AutomaticBean
public abstract class Check extends AbstractViolationReporter
{
/** resuable constant for message formating */
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
/** the current file contents */
private FileContents mFileContents = null;
@ -47,9 +45,6 @@ public abstract class Check extends AutomaticBean
/** the tab with for column reporting */
private int mTabWidth = 8; // meaningful default
/** the severity level of any violations found */
private SeverityLevel mSeverityLevel = SeverityLevel.DEFAULT_LEVEL;
/** current class loader */
private ClassLoader mLoader =
Thread.currentThread().getContextClassLoader();
@ -227,49 +222,6 @@ public abstract class Check extends AutomaticBean
mTabWidth = aTabWidth;
}
/**
* Returns the severity level of the check.
* @return the severity level
* @see com.puppycrawl.tools.checkstyle.api.SeverityLevel
*/
public final SeverityLevel getSeverityLevel()
{
return mSeverityLevel;
}
/**
* Sets the severity level. The string should be one of the names
* defined in the <code>SeverityLevel</code> class.
*
* @param aSeverity The new severity level
* @see com.puppycrawl.tools.checkstyle.api.SeverityLevel
*/
public void setSeverity(String aSeverity)
{
mSeverityLevel = SeverityLevel.getInstance(aSeverity);
}
/**
* Get the severity level's name.
*
* @return the check's severity level name.
*/
public String getSeverity()
{
return mSeverityLevel.getName();
}
/**
* Log an error message.
*
* @param aLine the line number where the error was found
* @param aKey the message that describes the error
*/
protected final void log(int aLine, String aKey)
{
log(aLine, aKey, EMPTY_OBJECT_ARRAY);
}
/**
* Log an error message.
*
@ -282,93 +234,10 @@ public abstract class Check extends AutomaticBean
protected final void log(int aLine, String aKey, Object aArgs[])
{
mMessages.add(new LocalizedMessage(
aLine, getResourceBundle(), aKey, aArgs, mSeverityLevel));
aLine, getMessageBundle(), aKey, aArgs, getSeverityLevel()));
}
/**
* Helper method to log a LocalizedMessage. Column defaults to 0.
*
* @param aLineNo line number to associate with the message
* @param aKey key to locale message format
* @param aArg0 first argument
*/
protected final void log(int aLineNo, String aKey, Object aArg0)
{
log(aLineNo, aKey, new Object[] {aArg0});
}
/**
* Helper method to log a LocalizedMessage. Column defaults to 0.
*
* @param aLineNo line number to associate with the message
* @param aKey key to locale message format
* @param aArg0 first argument
* @param aArg1 second argument
*/
protected final void log(int aLineNo, String aKey,
Object aArg0, Object aArg1)
{
log(aLineNo, aKey, new Object[] {aArg0, aArg1});
}
/**
* Helper method to log a LocalizedMessage. Column defaults to 0.
*
* @param aLineNo line number to associate with the message
* @param aKey key to locale message format
* @param aArg0 first argument
* @param aArg1 second argument
* @param aArg2 third argument
*/
protected final void log(int aLineNo, String aKey,
Object aArg0, Object aArg1, Object aArg2)
{
log(aLineNo, aKey, new Object[] {aArg0, aArg1, aArg2});
}
/**
* Helper method to log a LocalizedMessage.
*
* @param aLineNo line number to associate with the message
* @param aColNo column number to associate with the message
* @param aKey key to locale message format
*/
protected final void log(int aLineNo, int aColNo, String aKey)
{
log(aLineNo, aColNo, aKey, EMPTY_OBJECT_ARRAY);
}
/**
* Helper method to log a LocalizedMessage.
*
* @param aLineNo line number to associate with the message
* @param aColNo column number to associate with the message
* @param aKey key to locale message format
* @param aArg0 an <code>Object</code> value
*/
protected final void log(int aLineNo, int aColNo, String aKey,
Object aArg0)
{
log(aLineNo, aColNo, aKey, new Object[] {aArg0});
}
/**
* Helper method to log a LocalizedMessage.
*
* @param aLineNo line number to associate with the message
* @param aColNo column number to associate with the message
* @param aKey key to locale message format
* @param aArg0 an <code>Object</code> value
* @param aArg1 an <code>Object</code> value
*/
protected final void log(int aLineNo, int aColNo, String aKey,
Object aArg0, Object aArg1)
{
log(aLineNo, aColNo, aKey, new Object[] {aArg0, aArg1});
}
/**
* Helper method to log a LocalizedMessage.
*
@ -383,27 +252,8 @@ public abstract class Check extends AutomaticBean
final int col = 1 + Utils.lengthExpandedTabs(
getLines()[aLineNo - 1], aColNo, getTabWidth());
mMessages.add(new LocalizedMessage(
aLineNo, col, getResourceBundle(), aKey, aArgs, mSeverityLevel));
aLineNo, col, getMessageBundle(), aKey, aArgs, getSeverityLevel()));
}
/**
* The default implementation expects the resource files to be named
* messages.properties, messages_de.properties, etc. The file must
* be placed in the same package as the Check implementation.
*
* Example: If you write com/foo/MyCoolCheck, create resource files
* com/foo/messages.properties, com/foo/messages_de.properties, etc.
*
* @return name of a resource bundle that contains the messages
* used by this check
*/
private String getResourceBundle()
{
// PERF: check perf impact, maybe cache result
final String className = this.getClass().getName();
final String packageName =
className.substring(0, className.lastIndexOf('.'));
return packageName + "." + "messages";
}
}

View File

@ -56,6 +56,9 @@ public final class LocalizedMessage
/** the severity level **/
private final SeverityLevel mSeverityLevel;
/** the default severity level if one is not specified */
private static final SeverityLevel DEFAULT_SEVERITY = SeverityLevel.ERROR;
/** key for the message format **/
private final String mKey;
@ -127,7 +130,7 @@ public final class LocalizedMessage
mKey = aKey;
mArgs = aArgs;
mBundle = aBundle;
mSeverityLevel = SeverityLevel.DEFAULT_LEVEL;
mSeverityLevel = DEFAULT_SEVERITY;
}
/**

View File

@ -67,9 +67,6 @@ public final class SeverityLevel implements Comparable, Serializable
public static final SeverityLevel ERROR =
new SeverityLevel(SEVERITYCODE_ERROR, SEVERITYNAME_ERROR);
/** the default severity */
public static final SeverityLevel DEFAULT_LEVEL = ERROR;
/** map from level names to the respective level */
private static final Map NAME_TO_LEVEL = new HashMap();
static {

View File

@ -26,6 +26,7 @@ import java.util.HashSet;
import com.puppycrawl.tools.checkstyle.api.MessageDispatcher;
import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
import com.puppycrawl.tools.checkstyle.api.LocalizedMessages;
/**
* <p>
@ -67,11 +68,10 @@ public class PackageHtmlCheck extends AbstractFileSetCheck
final String path = packageHtml.getPath();
dispatcher.fireFileStarted(path);
if (!packageHtml.exists()) {
LocalizedMessage[] errors = new LocalizedMessage[1];
String bundle = getMessageBundle();
errors[0] = new LocalizedMessage(
0, bundle, "javadoc.packageHtml", null);
getMessageDispatcher().fireErrors(path, errors);
log(0, "javadoc.packageHtml");
final LocalizedMessages messageList = getMessageCollector();
final LocalizedMessage[] messages = messageList.getMessages();
getMessageDispatcher().fireErrors(path, messages);
}
dispatcher.fireFileFinished(path);
}

View File

@ -187,14 +187,12 @@ public class TranslationCheck extends AbstractFileSetCheck
// Remaining elements in the key set are missing in the current file
if (!keysClone.isEmpty()) {
for (Iterator it = keysClone.iterator(); it.hasNext();) {
Object[] key = new Object[]{it.next()};
LocalizedMessage[] errors = new LocalizedMessage[1];
final String bundle = getMessageBundle();
errors[0] = new LocalizedMessage(
0, bundle, "translation.missingKey", key);
getMessageDispatcher().fireErrors(path, errors);
Object key = it.next();
log(0, "translation.missingKey", key);
}
}
LocalizedMessage[] errors = getMessageCollector().getMessages();
getMessageDispatcher().fireErrors(path, errors);
dispatcher.fireFileFinished(path);
}
}