fxcop-agent/src/jetbrains/buildServer/fxcop/agent/FxCopFileProcessor.java (341 lines of code) (raw):
package jetbrains.buildServer.fxcop.agent;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.xml.XppReader;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Stack;
import jetbrains.buildServer.agent.SimpleBuildLogger;
import jetbrains.buildServer.agent.inspections.*;
import jetbrains.buildServer.log.Loggers;
import jetbrains.buildServer.util.StringUtil;
import org.jetbrains.annotations.NotNull;
public class FxCopFileProcessor {
private enum PassType {
ISSUES, RULES
}
private enum EntityType {
TARGET, RESOURCE, NAMESPACE, TYPE, MEMBER, ACCESSOR
}
private final Stack<String> myMessageInspectionId = new Stack<String>();
private final Stack<EntityType> myCurrentEntity = new Stack<EntityType>();
private String myCurrentTarget;
private String myCurrentResource;
private String myCurrentNamespace;
private String myCurrentType;
private String myCurrentMember;
private String myCurrentAccessor;
private PassType myCurrentPass;
private final File myFxCopReport;
private final SimpleBuildLogger myLogger;
private final String mySourceFilePrefixLower;
private final InspectionReporter myReporter;
private HierarchicalStreamReader myStream = null;
private int myErrorsCount, myWarningsCount;
public FxCopFileProcessor(@NotNull final File fxcopReport,
@NotNull final String sourceFilePrefix,
@NotNull final SimpleBuildLogger logger,
@NotNull final InspectionReporter reporter) {
myFxCopReport = fxcopReport;
myLogger = logger;
mySourceFilePrefixLower = sourceFilePrefix.toLowerCase().replace('\\', '/');
myReporter = reporter;
}
public void processReport() throws IOException {
myCurrentPass = PassType.RULES;
handleFile();
myCurrentPass = PassType.ISSUES;
handleFile();
}
private void handleFile() throws IOException {
Reader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(myFxCopReport), "UTF8"));
try {
try {
myStream = new XppReader(reader);
myCurrentEntity.clear();
handleChildren();
} finally {
if (myStream != null) myStream.close();
}
} finally {
reader.close();
}
}
private Method getHandlerMethod(final String tagName) throws NoSuchMethodException {
return getClass().getDeclaredMethod("handle" + tagName + "Tag");
}
private void handleChildren() {
while (myStream.hasMoreChildren()) {
myStream.moveDown();
final String nodeName = myStream.getNodeName();
try {
Method handler = getHandlerMethod(nodeName);
//handler.setAccessible(true);
handler.invoke(this);
} catch (NoSuchMethodException e) {
myLogger.message(getMessage(nodeName) + ": Unknown tag '" + nodeName + "'");
} catch (InvocationTargetException e) {
Loggers.AGENT.debug(getMessage(nodeName), e);
String additionalDetails = "";
if (!StringUtil.isEmpty(e.getMessage())) {
additionalDetails += e.toString();
}
if (e.getTargetException() != null) {
additionalDetails += (StringUtil.isEmpty(additionalDetails) ? "" : ", caused by: ") + e.getTargetException().toString();
}
myLogger.error(getMessage(nodeName) + (StringUtil.isEmpty(additionalDetails) ? "" : ": " + additionalDetails));
} catch (IllegalAccessException e) {
Loggers.AGENT.debug(getMessage(nodeName), e);
myLogger.error(getMessage(nodeName) + ":" + e.toString());
} catch (RuntimeException e) {
// Java 9
if (!"java.lang.reflect.InaccessibleObjectException".equals(e.getClass().getName())) throw e;
myLogger.message(getMessage(nodeName) + ": Unknown tag '" + nodeName + "'");
}
myStream.moveUp();
}
}
private String getMessage(final String nodeName) {
return "Error while processing FxCop report '" + myFxCopReport.getAbsolutePath() + "', tag " + nodeName;
}
private void handleNamespacesTag() {
handleChildren();
}
private void handleNamespaceTag() {
myCurrentNamespace = myStream.getAttribute("Name");
myCurrentEntity.push(EntityType.NAMESPACE);
handleChildren();
myCurrentEntity.pop();
myCurrentNamespace = null;
}
private void handleMessagesTag() {
handleChildren();
}
private void handleMessageTag() {
myMessageInspectionId.add(myStream.getAttribute("TypeName") +
" " + myStream.getAttribute("CheckId"));
handleChildren();
myMessageInspectionId.pop();
}
private String getEntitySpec() {
if (myCurrentEntity.isEmpty()) {
return "_none_/_none_";
}
switch (myCurrentEntity.peek()) {
case NAMESPACE:
return "_Namespaces_/" + myCurrentNamespace;
case TARGET:
return myCurrentTarget + "/_assembly_";
case RESOURCE:
return myCurrentTarget + "/_resources_/" + myCurrentResource;
case TYPE:
case MEMBER:
case ACCESSOR:
return myCurrentTarget + "/" + myCurrentNamespace.replace(".", "/") + "/" + myCurrentType;
default:
return "_unknown_/_unknown_";
}
}
private void handleIssueTag() {
if (myCurrentPass != PassType.ISSUES) {
return;
}
InspectionInstance info = new InspectionInstance();
final String path = myStream.getAttribute("Path");
final String file = myStream.getAttribute("File");
final String line = myStream.getAttribute("Line");
final String level = myStream.getAttribute("Level");
String inspectionMessage = reformatInOneLine(myStream.getValue());
if (!myCurrentEntity.isEmpty()) {
if (myCurrentEntity.peek() == EntityType.MEMBER) {
inspectionMessage += " (" + myCurrentMember + ")";
} else if (myCurrentEntity.peek() == EntityType.ACCESSOR) {
inspectionMessage += " (" + myCurrentAccessor + ")";
}
}
info.setMessage(inspectionMessage);
String inspectionFile = getEntitySpec();
if (!StringUtil.isEmptyOrSpaces(file)) {
if (StringUtil.isEmptyOrSpaces(path)) {
inspectionFile += " :: " + file;
} else {
String reportPath = path.replace('\\', '/');
if (reportPath.toLowerCase().startsWith(mySourceFilePrefixLower)) {
reportPath = reportPath.substring(mySourceFilePrefixLower.length());
}
reportPath = reportPath.replace('/', '|').replace("\\", "|");
if (reportPath.startsWith("|")) {
reportPath = reportPath.substring(1);
}
if (reportPath.length() > 0) {
inspectionFile += " :: " + reportPath + "|" + file;
} else {
inspectionFile += " :: " + file;
}
}
}
info.setFilePath(inspectionFile);
if (StringUtil.isEmptyOrSpaces(line)) {
info.setLine(0);
} else {
info.setLine(Integer.parseInt(line));
}
if (!StringUtil.isEmptyOrSpaces(level)) {
final InspectionSeverityValues apiLevel = convertLevel(level);
if (apiLevel == InspectionSeverityValues.ERROR) {
myErrorsCount++;
}
if (apiLevel == InspectionSeverityValues.WARNING) {
myWarningsCount++;
}
info.addAttribute(InspectionAttributesId.SEVERITY.toString(), Collections.singleton(apiLevel.toString()));
}
if (myMessageInspectionId.isEmpty()){
throw new IllegalArgumentException("No current message inspection ID while processing entry: path: '" + path + "', file: '" + file + "'. Broken report?");
}
info.setInspectionId(myMessageInspectionId.peek());
myReporter.reportInspection(info);
}
private InspectionSeverityValues convertLevel(String level) {
if (level.contains("Error")) {
return InspectionSeverityValues.ERROR;
}
if (level.contains("Warning")) {
return InspectionSeverityValues.WARNING;
}
return InspectionSeverityValues.INFO;
}
private void handleTargetsTag() {
handleChildren();
}
private void handleExceptionsTag() {
handleChildren();
}
private void handleExceptionTag() {
if (myCurrentPass != PassType.ISSUES) {
return;
}
final String keyword = myStream.getAttribute("Keyword");
final String kind = myStream.getAttribute("Kind");
final String treatAsWarning = myStream.getAttribute("TreatAsWarning");
String type = null, message = null, stacktrace = null;
while (myStream.hasMoreChildren()) {
myStream.moveDown();
if (myStream.getNodeName().equals("Type")) {
type = reformatInOneLine(myStream.getValue());
}
if (myStream.getNodeName().equals("ExceptionMessage")) {
message = reformatInOneLine(myStream.getValue());
}
if (myStream.getNodeName().equals("StackTrace")) {
stacktrace = myStream.getValue();
}
myStream.moveUp();
}
final boolean warningMessage = treatAsWarning != null && treatAsWarning.equals("True");
final StringBuilder descr = new StringBuilder("FxCop " + (warningMessage ? "warning" : "error") + ":");
if (keyword != null) {
descr.append(" Keyword=").append(keyword);
}
if (kind != null) {
descr.append(" Kind=").append(kind);
}
if (type != null) {
descr.append(" Type=").append(type);
}
if (message != null) {
descr.append(" ").append(message);
}
if (stacktrace != null) {
final String lineSeparator = System.getProperty("line.separator");
descr.append(lineSeparator).append(stacktrace);
}
myLogger.warning(descr.toString());
}
private void handleTargetTag() {
myCurrentTarget = new File(myStream.getAttribute("Name")).getName();
myCurrentEntity.push(EntityType.TARGET);
handleChildren();
myCurrentEntity.pop();
myCurrentTarget = null;
}
private void handleModulesTag() {
handleChildren();
}
private void handleModuleTag() {
handleChildren();
}
private void handleTypesTag() {
handleChildren();
}
private void handleTypeTag() {
myCurrentType = myStream.getAttribute("Name");
myCurrentEntity.push(EntityType.TYPE);
handleChildren();
myCurrentEntity.pop();
myCurrentType = null;
}
private void handleMembersTag() {
handleChildren();
}
private void handleMemberTag() {
myCurrentMember = myStream.getAttribute("Name");
myCurrentEntity.push(EntityType.MEMBER);
handleChildren();
myCurrentEntity.pop();
myCurrentMember = null;
}
private void handleAccessorsTag() {
handleChildren();
}
private void handleAccessorTag() {
myCurrentAccessor = myStream.getAttribute("Name");
myCurrentEntity.push(EntityType.ACCESSOR);
handleChildren();
myCurrentEntity.pop();
myCurrentAccessor = null;
}
private void handleResourcesTag() {
handleChildren();
}
private void handleResourceTag() {
myCurrentResource = myStream.getAttribute("Name");
myCurrentEntity.push(EntityType.RESOURCE);
handleChildren();
myCurrentEntity.pop();
myCurrentResource = null;
}
private void handleRulesTag() {
handleChildren();
}
private void handleLocalizedTag() {
// Do nothing
}
private void handleRuleTag() {
if (myCurrentPass != PassType.RULES) {
return;
}
final InspectionTypeInfo type = new InspectionTypeInfo();
type.setId(myStream.getAttribute("TypeName") + " " + myStream.getAttribute("CheckId"));
type.setCategory(myStream.getAttribute("Category"));
while (myStream.hasMoreChildren()) {
myStream.moveDown();
if (myStream.getNodeName().equals("Name")) {
type.setName(reformatInOneLine(myStream.getValue()));
}
if (myStream.getNodeName().equals("Description")) {
type.setDescription(reformatInOneLine(myStream.getValue()));
}
myStream.moveUp();
}
myReporter.reportInspectionType(type);
}
private String reformatInOneLine(@NotNull final String source) {
return source.replace("\r", "").replace("\n", " ").replaceAll("\\s+", " ").trim();
}
public int getErrorsCount() {
return myErrorsCount;
}
public int getWarningsCount() {
return myWarningsCount;
}
}