edu.umd.cs.findbugs.SAXBugCollectionHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
The newest version!
/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2004-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;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableSet;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import edu.umd.cs.findbugs.filter.AndMatcher;
import edu.umd.cs.findbugs.filter.AnnotationMatcher;
import edu.umd.cs.findbugs.filter.BugMatcher;
import edu.umd.cs.findbugs.filter.ClassMatcher;
import edu.umd.cs.findbugs.filter.CompoundMatcher;
import edu.umd.cs.findbugs.filter.ConfidenceMatcher;
import edu.umd.cs.findbugs.filter.FieldMatcher;
import edu.umd.cs.findbugs.filter.Filter;
import edu.umd.cs.findbugs.filter.FirstVersionMatcher;
import edu.umd.cs.findbugs.filter.LastVersionMatcher;
import edu.umd.cs.findbugs.filter.LocalMatcher;
import edu.umd.cs.findbugs.filter.Matcher;
import edu.umd.cs.findbugs.filter.MethodMatcher;
import edu.umd.cs.findbugs.filter.NotMatcher;
import edu.umd.cs.findbugs.filter.OrMatcher;
import edu.umd.cs.findbugs.filter.PriorityMatcher;
import edu.umd.cs.findbugs.filter.RankMatcher;
import edu.umd.cs.findbugs.filter.SourceMatcher;
import edu.umd.cs.findbugs.filter.TypeMatcher;
import edu.umd.cs.findbugs.model.ClassFeatureSet;
import edu.umd.cs.findbugs.util.MapCache;
import edu.umd.cs.findbugs.util.Strings;
/**
* Build a BugCollection based on SAX events. This is intended to replace the
* old DOM-based parsing of XML bug result files, which was very slow.
*
* @author David Hovemeyer
*/
public class SAXBugCollectionHandler extends DefaultHandler {
private static final Logger LOG = LoggerFactory.getLogger(SAXBugCollectionHandler.class);
private static final String FIND_BUGS_FILTER = "FindBugsFilter";
private static final String PROJECT = "Project";
private static final String BUG_COLLECTION = "BugCollection";
public String getOptionalAttribute(Attributes attributes, String qName) {
return memoized(attributes.getValue(qName));
}
@CheckForNull
private final BugCollection bugCollection;
@CheckForNull
private final Project project;
private final Stack matcherStack = new Stack<>();
private Filter filter;
private final MapCache cache = new MapCache<>(2000);
private final ArrayList elementStack;
private final StringBuilder textBuffer;
private BugInstance bugInstance;
private BugAnnotationWithSourceLines bugAnnotationWithSourceLines;
private AnalysisError analysisError;
// private ClassHash classHash;
private ClassFeatureSet classFeatureSet;
private final ArrayList stackTrace;
private int nestingOfIgnoredElements = 0;
private final @CheckForNull File base;
private final String topLevelName;
private final Map qnameCache = new HashMap<>();
private SAXBugCollectionHandler(String topLevelName, @CheckForNull BugCollection bugCollection,
@CheckForNull Project project, @CheckForNull File base) {
this.topLevelName = topLevelName;
this.bugCollection = bugCollection;
this.project = project;
this.elementStack = new ArrayList<>();
this.textBuffer = new StringBuilder();
this.stackTrace = new ArrayList<>();
this.base = base;
}
public SAXBugCollectionHandler(BugCollection bugCollection, @CheckForNull File base) {
this(BUG_COLLECTION, bugCollection, bugCollection.getProject(), base);
}
public SAXBugCollectionHandler(BugCollection bugCollection) {
this(BUG_COLLECTION, bugCollection, bugCollection.getProject(), null);
}
public SAXBugCollectionHandler(Project project, File base) {
this(PROJECT, null, project, base);
}
public SAXBugCollectionHandler(Filter filter, File base) {
this(FIND_BUGS_FILTER, null, null, base);
this.filter = filter;
pushCompoundMatcher(filter);
}
Pattern ignoredElement = Pattern.compile("Message|ShortMessage|LongMessage");
public boolean discardedElement(String qName) {
return ignoredElement.matcher(qName).matches();
}
public String getTextContents() {
return memoized(Strings.unescapeXml(textBuffer.toString()));
}
private String memoized(String s) {
if (s == null) {
return s;
}
String result = cache.get(s);
if (result != null) {
return result;
}
cache.put(s, s);
return s;
}
private static boolean DEBUG = false;
@Override
public void startElement(String uri, String name, String qName, Attributes attributes) throws SAXException {
// URI should always be empty.
// So, qName is the name of the element.
if (discardedElement(qName)) {
nestingOfIgnoredElements++;
} else if (nestingOfIgnoredElements > 0) {
// ignore it
} else {
// We should be parsing the outer BugCollection element.
if (elementStack.isEmpty() && !qName.equals(topLevelName)) {
throw new SAXException("Invalid top-level element (expected " + topLevelName + ", saw " + qName + ")");
}
if (BUG_COLLECTION.equals(qName)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
// Read and set the sequence number.
String version = getOptionalAttribute(attributes, "version");
if (bugCollection instanceof SortedBugCollection) {
bugCollection.setAnalysisVersion(version);
}
// Read and set the sequence number.
String sequence = getOptionalAttribute(attributes, "sequence");
long seqval = parseLong(sequence, 0L);
bugCollection.setSequenceNumber(seqval);
// Read and set timestamp.
String timestamp = getOptionalAttribute(attributes, "timestamp");
long tsval = parseLong(timestamp, -1L);
bugCollection.setTimestamp(tsval);
// Read and set timestamp.
String analysisTimestamp = getOptionalAttribute(attributes, "analysisTimestamp");
if (analysisTimestamp != null) {
bugCollection.setAnalysisTimestamp(parseLong(analysisTimestamp, -1L));
}
String analysisVersion = getOptionalAttribute(attributes, "version");
if (analysisVersion != null) {
bugCollection.setAnalysisVersion(analysisVersion);
}
// Set release name, if present.
String releaseName = getOptionalAttribute(attributes, "release");
bugCollection.setReleaseName((releaseName != null) ? releaseName : "");
} else if (isTopLevelFilter(qName)) {
if (project != null) {
filter = new Filter();
project.setSuppressionFilter(filter);
}
matcherStack.clear();
pushCompoundMatcher(filter);
} else if (PROJECT.equals(qName)) {
Project project = this.project;
assert project != null;
// Project element
String projectName = getOptionalAttribute(attributes, Project.PROJECTNAME_ATTRIBUTE_NAME);
if (projectName != null) {
project.setProjectName(projectName);
}
} else {
String outerElement = elementStack.get(elementStack.size() - 1);
if (BUG_COLLECTION.equals(outerElement)) {
// Parsing a top-level element of the BugCollection
if ("BugInstance".equals(qName)) {
// BugInstance element - get required type and priority
// attributes
String type = getRequiredAttribute(attributes, "type", qName);
String priority = getRequiredAttribute(attributes, "priority", qName);
try {
int prio = Integer.parseInt(priority);
bugInstance = new BugInstance(type, prio);
} catch (NumberFormatException e) {
throw new SAXException("BugInstance with invalid priority value \"" + priority + "\"", e);
}
String firstVersion = getOptionalAttribute(attributes, "first");
if (firstVersion != null) {
bugInstance.setFirstVersion(Long.parseLong(firstVersion));
}
String lastVersion = getOptionalAttribute(attributes, "last");
if (lastVersion != null) {
bugInstance.setLastVersion(Long.parseLong(lastVersion));
}
if (bugInstance.isDead() && bugInstance.getFirstVersion() > bugInstance.getLastVersion()) {
throw new IllegalStateException("huh");
}
String introducedByChange = getOptionalAttribute(attributes, "introducedByChange");
if (introducedByChange != null) {
bugInstance.setIntroducedByChangeOfExistingClass(Boolean.parseBoolean(introducedByChange));
}
String removedByChange = getOptionalAttribute(attributes, "removedByChange");
if (removedByChange != null) {
bugInstance.setRemovedByChangeOfPersistingClass(Boolean.parseBoolean(removedByChange));
}
String oldInstanceHash = getOptionalAttribute(attributes, "instanceHash");
if (oldInstanceHash == null) {
oldInstanceHash = getOptionalAttribute(attributes, "oldInstanceHash");
}
if (oldInstanceHash != null) {
bugInstance.setOldInstanceHash(oldInstanceHash);
}
} else if ("FindBugsSummary".equals(qName)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
String timestamp = getRequiredAttribute(attributes, "timestamp", qName);
String vmVersion = getOptionalAttribute(attributes, "vm_version");
String totalClasses = getOptionalAttribute(attributes, "total_classes");
if (totalClasses != null && totalClasses.length() > 0) {
bugCollection.getProjectStats().setTotalClasses(Integer.parseInt(totalClasses));
}
String totalSize = getOptionalAttribute(attributes, "total_size");
if (totalSize != null && totalSize.length() > 0) {
bugCollection.getProjectStats().setTotalSize(Integer.parseInt(totalSize));
}
String referencedClasses = getOptionalAttribute(attributes, "referenced_classes");
if (referencedClasses != null && referencedClasses.length() > 0) {
bugCollection.getProjectStats().setReferencedClasses(Integer.parseInt(referencedClasses));
}
bugCollection.getProjectStats().setVMVersion(vmVersion);
try {
bugCollection.getProjectStats().setTimestamp(timestamp);
} catch (java.text.ParseException e) {
throw new SAXException("Unparseable sequence number: '" + timestamp + "'", e);
}
}
} else if ("BugInstance".equals(outerElement)) {
parseBugInstanceContents(qName, attributes);
} else if ("Method".equals(outerElement) || "Field".equals(outerElement) || "Class".equals(outerElement)
|| "Type".equals(outerElement)) {
if ("SourceLine".equals(qName)) {
// package member elements can contain nested SourceLine
// elements.
bugAnnotationWithSourceLines.setSourceLines(createSourceLineAnnotation(qName, attributes));
}
} else if (BugCollection.ERRORS_ELEMENT_NAME.equals(outerElement)) {
if (BugCollection.ANALYSIS_ERROR_ELEMENT_NAME.equals(qName) || BugCollection.ERROR_ELEMENT_NAME.equals(qName)) {
analysisError = new AnalysisError("Unknown error");
stackTrace.clear();
}
} else if ("FindBugsSummary".equals(outerElement) && "PackageStats".equals(qName)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
String packageName = getRequiredAttribute(attributes, "package", qName);
int numClasses = Integer.parseInt(getRequiredAttribute(attributes, "total_types", qName));
int size = Integer.parseInt(getRequiredAttribute(attributes, "total_size", qName));
bugCollection.getProjectStats().putPackageStats(packageName, numClasses, size);
} else if ("PackageStats".equals(outerElement)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
if ("ClassStats".equals(qName)) {
String className = getRequiredAttribute(attributes, "class", qName);
Boolean isInterface = Boolean.valueOf(getRequiredAttribute(attributes, "interface", qName));
int size = Integer.parseInt(getRequiredAttribute(attributes, "size", qName));
String sourceFile = getOptionalAttribute(attributes, "sourceFile");
bugCollection.getProjectStats().addClass(className, sourceFile, isInterface, size, false);
}
} else if (isTopLevelFilter(outerElement) || isCompoundElementTag(outerElement)) {
parseMatcher(qName, attributes);
} else if ("ClassFeatures".equals(outerElement)) {
if (ClassFeatureSet.ELEMENT_NAME.equals(qName)) {
String className = getRequiredAttribute(attributes, "class", qName);
classFeatureSet = new ClassFeatureSet();
classFeatureSet.setClassName(className);
}
} else if (ClassFeatureSet.ELEMENT_NAME.equals(outerElement)) {
if (ClassFeatureSet.FEATURE_ELEMENT_NAME.equals(qName)) {
String value = getRequiredAttribute(attributes, "value", qName);
classFeatureSet.addFeature(value);
}
} else if (BugCollection.HISTORY_ELEMENT_NAME.equals(outerElement)) {
if (AppVersion.ELEMENT_NAME.equals(qName)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
try {
String sequence = getRequiredAttribute(attributes, "sequence", qName);
String timestamp = getOptionalAttribute(attributes, "timestamp");
String releaseName = getOptionalAttribute(attributes, "release");
String codeSize = getOptionalAttribute(attributes, "codeSize");
String numClasses = getOptionalAttribute(attributes, "numClasses");
AppVersion appVersion = new AppVersion(Long.parseLong(sequence));
if (timestamp != null) {
appVersion.setTimestamp(Long.parseLong(timestamp));
}
if (releaseName != null) {
appVersion.setReleaseName(releaseName);
}
if (codeSize != null) {
appVersion.setCodeSize(Integer.parseInt(codeSize));
}
if (numClasses != null) {
appVersion.setNumClasses(Integer.parseInt(numClasses));
}
bugCollection.addAppVersion(appVersion);
} catch (NumberFormatException e) {
throw new SAXException("Invalid AppVersion element", e);
}
}
} else if (BugCollection.PROJECT_ELEMENT_NAME.equals(outerElement)) {
Project project = this.project;
assert project != null;
if (Project.PLUGIN_ELEMENT_NAME.equals(qName)) {
String pluginId = getRequiredAttribute(attributes, Project.PLUGIN_ID_ATTRIBUTE_NAME, qName);
Boolean enabled = Boolean.valueOf(getRequiredAttribute(attributes, Project.PLUGIN_STATUS_ELEMENT_NAME, qName));
project.setPluginStatusTrinary(pluginId, enabled);
}
}
}
}
textBuffer.delete(0, textBuffer.length());
elementStack.add(qName);
}
private boolean isCompoundElementTag(String qName) {
return outerElementTags.contains(qName);
}
private boolean isTopLevelFilter(String qName) {
return FIND_BUGS_FILTER.equals(qName) || "SuppressionFilter".equals(qName);
}
private void addMatcher(Matcher m) {
if (m == null) {
throw new IllegalArgumentException("matcher must not be null");
}
CompoundMatcher peek = matcherStack.peek();
if (peek == null) {
throw new NullPointerException("Top of stack is null");
}
peek.addChild(m);
if (nextMatchedIsDisabled) {
if (peek instanceof Filter) {
((Filter) peek).disable(m);
} else {
assert false;
}
nextMatchedIsDisabled = false;
}
}
private void pushCompoundMatcherAsChild(CompoundMatcher m) {
addMatcher(m);
pushCompoundMatcher(m);
}
private void pushCompoundMatcher(CompoundMatcher m) {
if (m == null) {
throw new IllegalArgumentException("matcher must not be null");
}
matcherStack.push(m);
}
boolean nextMatchedIsDisabled;
private final Set outerElementTags = unmodifiableSet(new HashSet<>(asList("And", "Match", "Or", "Not")));
private void parseMatcher(String qName, Attributes attributes) throws SAXException {
if (DEBUG) {
System.out.println(elementStack + " " + qName + " " + matcherStack);
}
String disabled = getOptionalAttribute(attributes, "disabled");
nextMatchedIsDisabled = "true".equals(disabled);
if ("Bug".equals(qName)) {
addMatcher(new BugMatcher(getOptionalAttribute(attributes, "code"), getOptionalAttribute(attributes, "pattern"),
getOptionalAttribute(attributes, "category")));
} else if ("Class".equals(qName)) {
String role = getOptionalAttribute(attributes, "role");
addMatcher(new ClassMatcher(getRequiredAttribute(attributes, "name", qName), role));
} else if ("Type".equals(qName)) {
String role = getOptionalAttribute(attributes, "role");
String typeParameters = getOptionalAttribute(attributes, "typeParameters");
addMatcher(new TypeMatcher(getRequiredAttribute(attributes, "descriptor", qName), role, typeParameters));
} else if ("FirstVersion".equals(qName)) {
addMatcher(new FirstVersionMatcher(getRequiredAttribute(attributes, "value", qName), getRequiredAttribute(attributes,
"relOp", qName)));
} else if ("LastVersion".equals(qName)) {
addMatcher(new LastVersionMatcher(getRequiredAttribute(attributes, "value", qName), getRequiredAttribute(attributes,
"relOp", qName)));
} else if ("BugCode".equals(qName)) {
addMatcher(new BugMatcher(getRequiredAttribute(attributes, "name", qName), "", ""));
} else if ("Local".equals(qName)) {
addMatcher(new LocalMatcher(getRequiredAttribute(attributes, "name", qName)));
} else if ("BugPattern".equals(qName)) {
addMatcher(new BugMatcher("", getRequiredAttribute(attributes, "name", qName), ""));
} else if ("Priority".equals(qName)) {
addMatcher(new PriorityMatcher(getRequiredAttribute(attributes, "value", qName)));
} else if ("Confidence".equals(qName)) {
addMatcher(new ConfidenceMatcher(getRequiredAttribute(attributes, "value", qName)));
} else if ("Rank".equals(qName)) {
addMatcher(new RankMatcher(getRequiredAttribute(attributes, "value", qName)));
} else if ("Package".equals(qName)) {
String pName = getRequiredAttribute(attributes, "name", qName);
pName = pName.startsWith("~") ? pName : "~" + pName.replace(".", "\\.");
addMatcher(new ClassMatcher(pName + "\\.[^.]+"));
} else if ("Method".equals(qName)) {
String name = getOptionalAttribute(attributes, "name");
String params = getOptionalAttribute(attributes, "params");
String returns = getOptionalAttribute(attributes, "returns");
String role = getOptionalAttribute(attributes, "role");
addMatcher(new MethodMatcher(name, params, returns, role));
} else if ("Field".equals(qName)) {
String name = getOptionalAttribute(attributes, "name");
String type = getOptionalAttribute(attributes, "type");
String role = getOptionalAttribute(attributes, "role");
addMatcher(new FieldMatcher(name, type, role));
} else if ("Or".equals(qName)) {
CompoundMatcher matcher = new OrMatcher();
pushCompoundMatcherAsChild(matcher);
} else if ("And".equals(qName) || "Match".equals(qName)) {
AndMatcher matcher = new AndMatcher();
pushCompoundMatcherAsChild(matcher);
if ("Match".equals(qName)) {
String classregex = getOptionalAttribute(attributes, "classregex");
String classMatch = getOptionalAttribute(attributes, "class");
if (classregex != null) {
addMatcher(new ClassMatcher("~" + classregex));
} else if (classMatch != null) {
addMatcher(new ClassMatcher(classMatch));
}
}
} else if ("Not".equals(qName)) {
NotMatcher matcher = new NotMatcher();
pushCompoundMatcherAsChild(matcher);
} else if ("Source".equals(qName)) {
addMatcher(new SourceMatcher(getRequiredAttribute(attributes, "name", qName)));
} else if ("Annotation".equals(qName)) {
addMatcher(new AnnotationMatcher(getRequiredAttribute(attributes, "name", qName)));
}
nextMatchedIsDisabled = false;
}
private void parseBugInstanceContents(String qName, Attributes attributes) throws SAXException {
// Parsing an attribute or property of a BugInstance
BugAnnotation bugAnnotation = null;
if ("Class".equals(qName)) {
String className = getRequiredAttribute(attributes, "classname", qName);
String classAnnotationNames = getOptionalAttribute(attributes, "classAnnotationNames");
bugAnnotation = bugAnnotationWithSourceLines = new ClassAnnotation(className);
if (classAnnotationNames != null) {
((PackageMemberAnnotation) bugAnnotation)
.setJavaAnnotationNames(Arrays.asList(classAnnotationNames.split(",")));
}
} else if ("Type".equals(qName)) {
String typeDescriptor = getRequiredAttribute(attributes, "descriptor", qName);
TypeAnnotation typeAnnotation;
bugAnnotation = bugAnnotationWithSourceLines = typeAnnotation = new TypeAnnotation(typeDescriptor);
String typeParameters = getOptionalAttribute(attributes, "typeParameters");
if (typeParameters != null) {
typeAnnotation.setTypeParameters(Strings.unescapeXml(typeParameters));
}
} else if ("Method".equals(qName) || "Field".equals(qName)) {
String classname = getRequiredAttribute(attributes, "classname", qName);
String fieldOrMethodName = getRequiredAttribute(attributes, "name", qName);
String signature = getRequiredAttribute(attributes, "signature", qName);
String classAnnotationNames = getOptionalAttribute(attributes, "classAnnotationNames");
if ("Method".equals(qName)) {
String isStatic = getOptionalAttribute(attributes, "isStatic");
if (isStatic == null) {
isStatic = "false"; // Hack for old data
}
bugAnnotation = bugAnnotationWithSourceLines = new MethodAnnotation(classname, fieldOrMethodName, signature,
Boolean.valueOf(isStatic));
} else {
String isStatic = getRequiredAttribute(attributes, "isStatic", qName);
String sourceSignature = getOptionalAttribute(attributes, "sourceSignature");
bugAnnotation = bugAnnotationWithSourceLines = new FieldAnnotation(classname, fieldOrMethodName, signature,
sourceSignature, Boolean.valueOf(isStatic));
}
if (classAnnotationNames != null) {
((PackageMemberAnnotation) bugAnnotation)
.setJavaAnnotationNames(Arrays.asList(classAnnotationNames.split(",")));
}
} else if ("SourceLine".equals(qName)) {
SourceLineAnnotation sourceAnnotation = createSourceLineAnnotation(qName, attributes);
if (!sourceAnnotation.isSynthetic()) {
bugAnnotation = sourceAnnotation;
}
} else if ("Int".equals(qName)) {
try {
String value = getRequiredAttribute(attributes, "value", qName);
bugAnnotation = new IntAnnotation(Integer.parseInt(value));
} catch (NumberFormatException e) {
throw new SAXException("Bad integer value in Int");
}
} else if ("String".equals(qName)) {
String value = getRequiredAttribute(attributes, "value", qName);
bugAnnotation = StringAnnotation.fromXMLEscapedString(value);
} else if ("LocalVariable".equals(qName)) {
try {
String varName = getRequiredAttribute(attributes, "name", qName);
int register = Integer.parseInt(getRequiredAttribute(attributes, "register", qName));
int pc = Integer.parseInt(getRequiredAttribute(attributes, "pc", qName));
bugAnnotation = new LocalVariableAnnotation(varName, register, pc);
} catch (NumberFormatException e) {
throw new SAXException("Invalid integer value in attribute of LocalVariable element");
}
} else if ("Property".equals(qName)) {
// A BugProperty.
String propName = getRequiredAttribute(attributes, "name", qName);
String propValue = getRequiredAttribute(attributes, "value", qName);
bugInstance.setProperty(propName, propValue);
} else if ("UserAnnotation".equals(qName)) {
// legacy from the cloud plugin stuff
} else {
// @Anemone, add custom bug annotation types,
// which can be deserialized with 'fromXML' method in its class.
Method fromXML = qnameCache.computeIfAbsent(qName, k -> {
for (Plugin plugin : Plugin.getAllPlugins()) {
Class> annotationClazz;
try {
// The qName should equal to its classname, so we can reflect the class by 'qName'.
annotationClazz = plugin.getClassLoader().loadClass(k);
return annotationClazz.getMethod("fromXML", String.class, Attributes.class);
} catch (NoSuchMethodException | ClassCastException | ClassNotFoundException ignored) {
LOG.warn("{} not found in Plugin({})", k, plugin.getPluginId(), ignored);
// The current plugin classloader doesn't have the annotation class called 'qName', ignore.
}
}
return null;
});
if (fromXML == null) {
throw new SAXException("Unknown bug annotation named " + qName);
}
try {
bugAnnotation = (BugAnnotation) fromXML.invoke(null, qName, attributes);
} catch (IllegalAccessException e) {
throw new SAXException("Factory method for " + qName + " is not accessible.", e);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof Exception) {
throw new SAXException("Factory method for " + qName + " threw an exception.", (Exception) e.getTargetException());
} else {
throw new SAXException("Factory method for " + qName + " threw an exception:\n" + Arrays.toString(e.getTargetException()
.getStackTrace()));
}
}
}
if (bugAnnotation != null) {
setAnnotationRole(attributes, bugAnnotation);
bugInstance.add(bugAnnotation);
}
}
private long parseLong(String s, long defaultValue) {
long value;
try {
value = (s != null) ? Long.parseLong(s) : defaultValue;
} catch (NumberFormatException e) {
value = defaultValue;
}
return value;
}
/*
* Extract a hash value from an element.
*
* @param qName
* name of element containing hash value
* @param attributes
* element attributes
* @return the decoded hash value
* @throws SAXException
*
private byte[] extractHash(String qName, Attributes attributes) throws SAXException {
String encodedHash = getRequiredAttribute(attributes, "value", qName);
byte[] hash;
try {
// System.out.println("Extract hash " + encodedHash);
hash = ClassHash.stringToHash(encodedHash);
} catch (IllegalArgumentException e) {
throw new SAXException("Invalid class hash", e);
}
return hash;
}*/
private void setAnnotationRole(Attributes attributes, BugAnnotation bugAnnotation) {
String role = getOptionalAttribute(attributes, "role");
if (role != null) {
bugAnnotation.setDescription(role);
}
}
private SourceLineAnnotation createSourceLineAnnotation(String qName, Attributes attributes) throws SAXException {
String classname = getRequiredAttribute(attributes, "classname", qName);
String sourceFile = getOptionalAttribute(attributes, "sourcefile");
if (sourceFile == null) {
sourceFile = SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
}
String startLine = getOptionalAttribute(attributes, "start"); // "start"/"end"
// are now
// optional
String endLine = getOptionalAttribute(attributes, "end"); // (were too
// many "-1"s
// in the xml)
String startBytecode = getOptionalAttribute(attributes, "startBytecode");
String endBytecode = getOptionalAttribute(attributes, "endBytecode");
String synthetic = getOptionalAttribute(attributes, "synthetic");
try {
int sl = startLine != null ? Integer.parseInt(startLine) : -1;
int el = endLine != null ? Integer.parseInt(endLine) : -1;
int sb = startBytecode != null ? Integer.parseInt(startBytecode) : -1;
int eb = endBytecode != null ? Integer.parseInt(endBytecode) : -1;
SourceLineAnnotation s = new SourceLineAnnotation(classname, sourceFile, sl, el, sb, eb);
if ("true".equals(synthetic)) {
s.setSynthetic(true);
}
return s;
} catch (NumberFormatException e) {
throw new SAXException("Bad integer value in SourceLine element", e);
}
}
List sourceDirs = new ArrayList<>();
@Override
public void endElement(String uri, String name, String qName) throws SAXException {
// URI should always be empty.
// So, qName is the name of the element.
if (discardedElement(qName)) {
nestingOfIgnoredElements--;
} else if (nestingOfIgnoredElements > 0) {
// ignore it
} else if ("Project".equals(qName)) {
assert project != null;
project.addSourceDirs(sourceDirs);
} else if (elementStack.size() > 1) {
String outerElement = elementStack.get(elementStack.size() - 2);
if (isTopLevelFilter(qName) || isCompoundElementTag(qName)) {
if (DEBUG) {
System.out.println(" ending " + elementStack + " " + qName + " " + matcherStack);
}
matcherStack.pop();
} else if (BUG_COLLECTION.equals(outerElement)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
if ("BugInstance".equals(qName)) {
bugCollection.add(bugInstance, false);
}
} else if (PROJECT.equals(outerElement)) {
Project project = this.project;
assert project != null;
if ("Jar".equals(qName)) {
project.addFile(makeAbsolute(getTextContents()));
} else if ("SrcDir".equals(qName)) {
sourceDirs.add(makeAbsolute(getTextContents()));
} else if ("AuxClasspathEntry".equals(qName)) {
project.addAuxClasspathEntry(makeAbsolute(getTextContents()));
}
} else if (BugCollection.ERRORS_ELEMENT_NAME.equals(outerElement)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
if (BugCollection.ANALYSIS_ERROR_ELEMENT_NAME.equals(qName)) {
analysisError.setMessage(getTextContents());
bugCollection.addError(analysisError);
} else if (BugCollection.ERROR_ELEMENT_NAME.equals(qName)) {
if (stackTrace.size() > 0) {
analysisError.setStackTrace(stackTrace.toArray(new String[0]));
}
bugCollection.addError(analysisError);
} else if (BugCollection.MISSING_CLASS_ELEMENT_NAME.equals(qName)) {
bugCollection.addMissingClass(getTextContents());
}
} else if (BugCollection.ERROR_ELEMENT_NAME.equals(outerElement)) {
if (BugCollection.ERROR_MESSAGE_ELEMENT_NAME.equals(qName)) {
analysisError.setMessage(getTextContents());
} else if (BugCollection.ERROR_EXCEPTION_ELEMENT_NAME.equals(qName)) {
analysisError.setExceptionMessage(getTextContents());
} else if (BugCollection.ERROR_STACK_TRACE_ELEMENT_NAME.equals(qName)) {
stackTrace.add(getTextContents());
}
} else if ("ClassFeatures".equals(outerElement)) {
if (ClassFeatureSet.ELEMENT_NAME.equals(qName)) {
BugCollection bugCollection = this.bugCollection;
assert bugCollection != null;
bugCollection.setClassFeatureSet(classFeatureSet);
classFeatureSet = null;
}
}
}
elementStack.remove(elementStack.size() - 1);
}
private String makeAbsolute(String possiblyRelativePath) {
if (possiblyRelativePath.contains("://") || possiblyRelativePath.startsWith("http:")
|| possiblyRelativePath.startsWith("https:") || possiblyRelativePath.startsWith("file:")) {
return possiblyRelativePath;
}
if (base == null) {
return possiblyRelativePath;
}
if (new File(possiblyRelativePath).isAbsolute()) {
return possiblyRelativePath;
}
return new File(base.getParentFile(), possiblyRelativePath).getAbsolutePath();
}
@Override
public void characters(char[] ch, int start, int length) {
textBuffer.append(ch, start, length);
}
private String getRequiredAttribute(Attributes attributes, String attrName, String elementName) throws SAXException {
String value = attributes.getValue(attrName);
if (value == null) {
throw new SAXException(elementName + " element missing " + attrName + " attribute");
}
return memoized(Strings.unescapeXml(value));
}
}