/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2005 Dave Brosius <dbrosius@users.sourceforge.net>
* Copyright (C) 2005 University of Maryland
*
* 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 edu.umd.cs.findbugs.detect;
import java.util.HashSet;
import java.util.Set;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.JavaClass;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.internalAnnotations.StaticConstant;
public class XMLFactoryBypass extends BytecodeScanningDetector {
private final BugReporter bugReporter;
@StaticConstant
private static final Set<String> xmlInterfaces = new HashSet<String>() {
static final long serialVersionUID = -9117982073509840017L;
{
add("javax.xml.parsers.DocumentBuilder");
add("org.w3c.dom.Document");
add("javax.xml.parsers.SAXParser");
add("org.xml.sax.XMLReader");
add("org.xml.sax.XMLFilter");
add("javax.xml.transform.Transformer");
add("org.w3c.dom.Attr");
add("org.w3c.dom.CDATASection");
add("org.w3c.dom.Comment");
add("org.w3c.dom.Element");
add("org.w3c.dom.Text");
}
};
private final Set<String> rejectedXMLClasses = new HashSet<String>();
private JavaClass curClass;
public XMLFactoryBypass(BugReporter bugReporter) {
this.bugReporter = bugReporter;
}
@Override
public void visitClassContext(ClassContext classContext) {
curClass = classContext.getJavaClass();
super.visitClassContext(classContext);
}
@Override
public void sawOpcode(int seen) {
try {
if (seen == INVOKESPECIAL) {
String newClsName = getClassConstantOperand();
if (rejectedXMLClasses.contains(newClsName))
return;
rejectedXMLClasses.add(newClsName);
if (newClsName.startsWith("java/") || newClsName.startsWith("javax/"))
return;
if (newClsName.endsWith("Adapter"))
return;
if (!getNameConstantOperand().equals("<init>"))
return;
String invokerClsName = this.getClassName();
if (samePackageBase(invokerClsName, newClsName))
return;
JavaClass newCls = Repository.lookupClass(getDottedClassConstantOperand());
JavaClass superCls = curClass.getSuperClass();
if (superCls.getClassName().equals(newClsName.replace('/', '.')))
return;
JavaClass[] infs = newCls.getAllInterfaces();
for (JavaClass inf : infs) {
if (xmlInterfaces.contains(inf.getClassName())) {
bugReporter.reportBug(new BugInstance(this, "XFB_XML_FACTORY_BYPASS", LOW_PRIORITY).addClassAndMethod(
this).addSourceLine(this));
rejectedXMLClasses.remove(newClsName);
}
}
}
} catch (ClassNotFoundException cnfe) {
bugReporter.reportMissingClass(cnfe);
}
}
public boolean samePackageBase(String invokerClsName, String newClsName) {
String[] invokerParts = invokerClsName.split("/");
String[] newClsParts = newClsName.split("/");
if (newClsParts.length < 3)
return false;
if (invokerParts.length < 3)
return false;
if (!invokerParts[0].equals(newClsParts[0]))
return false;
return invokerParts[1].equals(newClsParts[1]);
}
}