package edu.umd.cs.findbugs.detect;
import org.apache.bcel.classfile.Code;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.ba.AnnotationDatabase;
import edu.umd.cs.findbugs.ba.AnnotationEnumeration;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
/**
* <p>
* Finds invocations of JDK methods that rely on the default platform encoding.
* </p>
* <p>
* If a Java application assumes that the default platform encoding is
* acceptable, the app's behaviour will vary from platform to platform. In
* particular, conversions between byte[] and java.lang.String (in either
* direction) may yield inconsistent results. To ensure Java code is portable,
* the desired encoding should be specified explicitly wherever such a
* conversion takes place.
* </p>
* <p>
* This FindBugs pattern detects invocations of Java Class Library methods and
* constructors that are known to use the default platform encoding.
* </p>
*
* @author Robin Fernandes
*/
public class DefaultEncodingDetector extends OpcodeStackDetector {
private final BugReporter bugReporter;
private DefaultEncodingAnnotationDatabase defaultEncodingAnnotationDatabase;
/**
* This annotation is used to denote a method which relies on the default
* platform encoding.
*/
static class DefaultEncodingAnnotation extends AnnotationEnumeration<DefaultEncodingAnnotation> {
public final static DefaultEncodingAnnotation DEFAULT_ENCODING = new DefaultEncodingAnnotation("DefaultEncoding", 1);
private final static DefaultEncodingAnnotation[] myValues = { DEFAULT_ENCODING };
public static DefaultEncodingAnnotation[] values() {
return myValues.clone();
}
private DefaultEncodingAnnotation(String s, int i) {
super(s, i);
}
}
/**
* Sets up and stores DefaultEncodingAnnotations on JCL methods.
*/
static class DefaultEncodingAnnotationDatabase extends AnnotationDatabase<DefaultEncodingAnnotation> {
public DefaultEncodingAnnotationDatabase() {
this.setAddClassOnly(false);
this.loadAuxiliaryAnnotations();
}
@Override
public void loadAuxiliaryAnnotations() {
addMethodAnnotation("java.lang.String", "getBytes", "()[B", false, DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.lang.String", "<init>", "([B)V", false, DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.lang.String", "<init>", "([BII)V", false, DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.ByteArrayOutputStream", "toString", "()Ljava/lang/String;", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileReader", "<init>", "(Ljava/lang/String;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileReader", "<init>", "(Ljava/io/File;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileReader", "<init>", "(Ljava/io/FileDescriptor;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileWriter", "<init>", "(Ljava/lang/String;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileWriter", "<init>", "(Ljava/lang/String;Z)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileWriter", "<init>", "(Ljava/io/File;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileWriter", "<init>", "(Ljava/io/File;Z)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.FileWriter", "<init>", "(Ljava/io/FileDescriptor;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.InputStreamReader", "<init>", "(Ljava/io/InputStream;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.OutputStreamWriter", "<init>", "(Ljava/io/OutputStream;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintStream", "<init>", "(Ljava/io/File;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintStream", "<init>", "(Ljava/io/OutputStream;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintStream", "<init>", "(Ljava/io/OutputStream;Z)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintStream", "<init>", "(Ljava/lang/String;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintWriter", "<init>", "(Ljava/io/File;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintWriter", "<init>", "(Ljava/io/OutputStream;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintWriter", "<init>", "(Ljava/io/OutputStream;Z)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.io.PrintWriter", "<init>", "(Ljava/lang/String;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.util.Scanner", "<init>", "(Ljava/io/File;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.util.Scanner", "<init>", "(Ljava/io/InputStream;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.util.Scanner", "<init>", "(Ljava/nio/channels/ReadableByteChannel;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.util.Formatter", "<init>", "(Ljava/lang/String;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.util.Formatter", "<init>", "(Ljava/io/File;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
addMethodAnnotation("java.util.Formatter", "<init>", "(Ljava/io/OutputStream;)V", false,
DefaultEncodingAnnotation.DEFAULT_ENCODING);
}
}
public DefaultEncodingDetector(BugReporter bugReporter) {
this.bugReporter = bugReporter;
this.defaultEncodingAnnotationDatabase = new DefaultEncodingAnnotationDatabase();
}
@Override
public void sawOpcode(int seen) {
switch (seen) {
case INVOKEVIRTUAL:
case INVOKESPECIAL:
case INVOKESTATIC:
XMethod callSeen = XFactory.createXMethod(MethodAnnotation.fromCalledMethod(this));
DefaultEncodingAnnotation annotation = defaultEncodingAnnotationDatabase.getDirectAnnotation(callSeen);
if (annotation != null) {
bugReporter.reportBug(new BugInstance(this, "DM_DEFAULT_ENCODING", HIGH_PRIORITY).addClassAndMethod(this)
.addCalledMethod(this).addSourceLine(this));
}
}
}
}