Monday, April 07, 2008

Locale.getDefault() FindBugs Detector

I just hacked together a little FindBugs plugin that will just detect calls to the static Locale.getDefault() method. This may be undesirable for application code to do - e.g. in cases where you have an application framework that provides a more flexible way of localization.

This is nothing I deem worthy of contributing to the FindBugs team, because technically there is nothing wrong with Locale.getDefault(). Nevertheless maybe you can use it, too.

Just drop it into the plugin directory of your FindBugs installation. The plugin contains the source code of its single class inside the jar. Because this page is not yet full, I post it here as well for a quick glance:

package de.shipdown.fb.detect;

import java.util.Locale;

import org.apache.bcel.classfile.Code;
import org.apache.bcel.generic.ObjectType;

import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;

/**
 * This is a FindBugs plugin designed to detect calls to the
 * {@link Locale#getDefault()} method. This may be undesirable in certain
 * contexts.
 * 
 * @author Daniel Schneller
 */
public class LocaleGetDefaultDetector extends OpcodeStackDetector {

	/** The reporter to report to */
	final private BugReporter reporter;
	final private BugAccumulator bugAccumulator;

	/**
	 * {@link ObjectType} for {@link java.util.Locale}
	 */
	private final ClassDescriptor localeType = DescriptorFactory
			.createClassDescriptor("java/util/Locale");

	/**
	 * Creates a new instance of this Detector.
	 * 
	 * @param aReporter
	 *            {@link BugReporter} instance to report found problems to.
	 */
	public LocaleGetDefaultDetector(BugReporter aReporter) {
		reporter = aReporter;
		bugAccumulator = new BugAccumulator(reporter);
	}

	Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext()
			.getSubtypes2();

	@Override
	public void visit(Code obj) {
		super.visit(obj);
		bugAccumulator.reportAccumulatedBugs();
	}

	/**
	 * Checks for method invocations ({@link org.apache.bcel.generic.INVOKESTATIC})
	 * on a {@link Locale#getDefault()}
	 * 
	 * @param seen
	 *            An opcode to be analyzed
	 * @see edu.umd.cs.findbugs.visitclass.DismantleBytecode#sawOpcode(int)
	 */
	@Override
	public void sawOpcode(int seen) {
		// we are only interested in static method calls
		if (seen != INVOKESTATIC) {
			return;
		}

		try {
			String className = getClassConstantOperand();
			if (className.startsWith("[")) {
				// Ignore array classes
				return;
			}
			ClassDescriptor cDesc = DescriptorFactory
					.createClassDescriptor(className);

			// if it is not compatible with Locale we are not
			// interested anymore
			if (!subtypes2.isSubtype(cDesc, localeType)) {
				return;
			}

			String invokedName = getNameConstantOperand();
			if (!invokedName.equals("getDefault")) {
				return;
			}

			bugAccumulator.accumulateBug(new BugInstance(this,
					"LOCALE_GETDEFAULT", NORMAL_PRIORITY).addClassAndMethod(
					this).addCalledMethod(this), this);

		} catch (ClassNotFoundException e) {
			AnalysisContext.reportMissingClass(e);
		}
	}

}

The file can be downloaded here: LocaleGetDefaultDetector.jar

No comments: