package hudson.plugins.violations.types.fxcop; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Locale; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import hudson.plugins.violations.ViolationsParser; import hudson.plugins.violations.model.FullBuildModel; import hudson.plugins.violations.model.FullFileModel; import hudson.plugins.violations.model.Severity; import hudson.plugins.violations.model.Violation; /** * Parses a fxcop xml report file. * * * This does not uses the XML Pull parser as it can not handle the FxCop XML * files. The bug is registered at Sun as http: //bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 */ public class FxCopParser implements ViolationsParser { private transient FullBuildModel model; private transient File projectPath; private transient FxCopRuleSet ruleSet = new FxCopRuleSet(); public void parse(FullBuildModel model, File projectPath, String fileName, String[] sourcePaths) throws IOException { this.projectPath = projectPath; this.model = model; DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder; try { docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.parse(new FileInputStream(new File(projectPath, fileName))); NodeList mainNode = doc.getElementsByTagName("FxCopReport"); Element rootElement = (Element) mainNode.item(0); parseRules(XmlElementUtil.getFirstElementByTagName(rootElement, "Rules")); parseNamespaces(XmlElementUtil.getFirstElementByTagName(rootElement, "Namespaces"), null); parseTargets(XmlElementUtil.getFirstElementByTagName(rootElement, "Targets")); // TODO parse notes } catch (ParserConfigurationException pce) { throw new IOException(pce); } catch (SAXException se) { throw new IOException(se); } } private void parseRules(Element rulesElement) { if (rulesElement != null) { for (Element rule : XmlElementUtil.getNamedChildElements(rulesElement, "Rule")) { ruleSet.addRule(rule); } } } private void parseTargets(Element targetsElement) { if (targetsElement != null) { for (Element target : XmlElementUtil.getNamedChildElements(targetsElement, "Target")) { String name = getString(target, "Name"); parseMessages(XmlElementUtil.getFirstElementByTagName(target, "Messages"), name); parseModules(XmlElementUtil.getFirstElementByTagName(target, "Modules"), name); parseResources(XmlElementUtil.getFirstElementByTagName(target, "Resources"), name); } } } // TODO test private void parseResources(Element resources, String parentName) { if (resources != null) { for (Element target : XmlElementUtil.getNamedChildElements(resources, "Resource")) { String name = getString(target, "Name"); parseMessages(XmlElementUtil.getFirstElementByTagName(target, "Messages"), name); } } } private void parseModules(Element modulesElement, String parentName) { if (modulesElement != null) { for (Element module : XmlElementUtil.getNamedChildElements(modulesElement, "Module")) { String name = getString(module, "Name"); parseMessages(XmlElementUtil.getFirstElementByTagName(module, "Messages"), name); parseNamespaces(XmlElementUtil.getFirstElementByTagName(module, "Namespaces"), name); } } } private void parseNamespaces(Element namespacesElement, String parentName) { if (namespacesElement != null) { for (Element namespace : XmlElementUtil.getNamedChildElements(namespacesElement, "Namespace")) { String name = getString(namespace, "Name"); parseMessages(XmlElementUtil.getFirstElementByTagName(namespace, "Messages"), name); parseTypes(XmlElementUtil.getFirstElementByTagName(namespace, "Types"), name); } } } private void parseTypes(Element typesElement, String parentName) { if (typesElement != null) { for (Element type : XmlElementUtil.getNamedChildElements(typesElement, "Type")) { String name = parentName + "." + getString(type, "Name"); // Kind, ExternallyVisible, Accessibility parseMessages(XmlElementUtil.getFirstElementByTagName(type, "Messages"), name); parseMembers(XmlElementUtil.getFirstElementByTagName(type, "Members"), name); } } } private void parseMembers(Element membersElement, String parentName) { if (membersElement != null) { for (Element member : XmlElementUtil.getNamedChildElements(membersElement, "Member")) { parseMember(member, parentName); } } } private void parseAccessors(Element accessorsElement, String parentName) { if (accessorsElement != null) { for (Element member : XmlElementUtil.getNamedChildElements(accessorsElement, "Accessor")) { parseMember(member, parentName); } } } private void parseMember(Element member, String parentName) { // Kind, ExternallyVisible, Accessibility, Static parseMessages(XmlElementUtil.getFirstElementByTagName(member, "Messages"), parentName); parseAccessors(XmlElementUtil.getFirstElementByTagName(member, "Accessors"), parentName); } private void parseMessages(Element messages, String parentName) { parseMessages(messages, parentName, null); } private void parseMessages(Element messages, String parentName, String subName) { if (messages != null) { for (Element message : XmlElementUtil.getNamedChildElements(messages, "Message")) { // Status, Id for (Element issue : XmlElementUtil.getNamedChildElements(message, "Issue")) { parseIssue(issue, message, parentName, subName); } // TODO, parse notes } } } private void parseIssue(Element issue, Element parent, String parentName, String subName) { // Path, File, Line, Level String typeName = getString(parent, "TypeName"); String category = getString(parent, "Category"); String checkId = getString(parent, "CheckId"); Violation violation = new Violation(); violation.setType("fxcop"); violation.setSource(category + "#" + checkId); setSeverityLevel(violation, getString(issue, "Level")); StringBuilder msgBuilder = new StringBuilder(); if (subName != null) { msgBuilder.append(subName); msgBuilder.append(" "); } FxCopRule rule = ruleSet.getRule(category, checkId); if (rule != null) { msgBuilder.append("<a href=\""); msgBuilder.append(rule.getUrl()); msgBuilder.append("\">"); msgBuilder.append(typeName); msgBuilder.append("</a>"); violation.setPopupMessage(rule.getDescription()); } else { msgBuilder.append(typeName); } msgBuilder.append(" - "); msgBuilder.append(issue.getTextContent()); violation.setMessage(msgBuilder.toString()); String filePath = getString(issue, "Path"); String fileName = getString(issue, "File"); String fileLine = getString(issue, "Line"); FullFileModel fileModel; if ((filePath.length() > 0) && (fileName.length() > 0) && (fileLine.length() > 0)) { violation.setLine(fileLine); //fileModel = model.getFileModel(filePath.substring(2) + "/" + fileName); fileModel = model.getFileModel(resolveName(filePath + File.separatorChar + fileName)); if (fileModel.getSourceFile() == null) { File sourceFile = new File(filePath, fileName); if (sourceFile.exists()) { fileModel.setSourceFile(sourceFile); fileModel.setLastModified(sourceFile.lastModified()); } } } else { fileModel = model.getFileModel(parentName); } fileModel.addViolation(violation); } private String resolveName(String absoluteName) { String remote = projectPath.getAbsolutePath().replace('\\', '/'); String lcRemote = remote.toLowerCase(Locale.US); String name = absoluteName.replace('\\', '/'); String lcName = name.toLowerCase(Locale.US); if (lcName.startsWith(lcRemote)) { name = name.substring(lcRemote.length()); } else { // remove dos drive name = name.substring(2); } // if name starts with a / strip it. if (name.startsWith("/")) { name = name.substring(1); } return name; } private String getString(Element element, String name) { if (element.hasAttribute(name)) { return element.getAttribute(name); } else { return ""; } } private void setSeverityLevel(Violation violation, String issueLevel) { if (issueLevel.contains("CriticalError")) { violation.setSeverity(Severity.HIGH); violation.setSeverityLevel(Severity.HIGH_VALUE); } else if (issueLevel.contains("Error")) { violation.setSeverity(Severity.MEDIUM_HIGH); violation.setSeverityLevel(Severity.MEDIUM_HIGH_VALUE); } else if (issueLevel.contains("CriticalWarning")) { violation.setSeverity(Severity.MEDIUM); violation.setSeverityLevel(Severity.MEDIUM_VALUE); } else if (issueLevel.contains("Warning")) { violation.setSeverity(Severity.MEDIUM_LOW); violation.setSeverityLevel(Severity.MEDIUM_LOW_VALUE); } else { violation.setSeverity(Severity.LOW); violation.setSeverityLevel(Severity.LOW_VALUE); } } }