001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.api.batch; 021 022 import org.apache.commons.io.IOUtils; 023 import static org.apache.commons.lang.StringUtils.isNotBlank; 024 import org.codehaus.staxmate.in.SMEvent; 025 import org.codehaus.staxmate.in.SMHierarchicCursor; 026 import org.codehaus.staxmate.in.SMInputCursor; 027 import org.sonar.api.profiles.RulesProfile; 028 import org.sonar.api.resources.Resource; 029 import org.sonar.api.rules.Rule; 030 import org.sonar.api.rules.RulesManager; 031 import org.sonar.api.rules.Violation; 032 import org.sonar.api.utils.ParsingUtils; 033 import org.sonar.api.utils.StaxParser; 034 035 import java.io.File; 036 import java.io.FileInputStream; 037 import java.io.FileNotFoundException; 038 import java.io.InputStream; 039 import java.text.ParseException; 040 import javax.xml.stream.XMLStreamException; 041 042 /** 043 * @since 1.10 044 */ 045 public abstract class AbstractViolationsStaxParser { 046 protected RulesManager rulesManager; 047 protected SensorContext context; 048 049 /** 050 * @deprecated since 1.11. 051 */ 052 @Deprecated 053 protected AbstractViolationsStaxParser(SensorContext context, RulesManager rulesManager, RulesProfile profile) { 054 this.rulesManager = rulesManager; 055 this.context = context; 056 } 057 058 protected AbstractViolationsStaxParser(SensorContext context, RulesManager rulesManager) { 059 this.rulesManager = rulesManager; 060 this.context = context; 061 } 062 063 /** 064 * Cursor for child resources to parse, the returned input cursor should be filtered on SMEvent.START_ELEMENT 065 * for optimal perfs 066 * 067 * @param rootCursor the root xml doc cursor 068 * @return a cursor with child resources elements to parse 069 */ 070 protected abstract SMInputCursor cursorForResources(SMInputCursor rootCursor) throws XMLStreamException; 071 072 /** 073 * Cursor for violations to parse for a given resource, the returned input cursor should be filtered on SMEvent.START_ELEMENT 074 * for optimal perfs 075 * 076 * @param resourcesCursor the current resource cursor 077 * @return a cursor with child violations elements to parse 078 */ 079 protected abstract SMInputCursor cursorForViolations(SMInputCursor resourcesCursor) throws XMLStreamException; 080 081 /** 082 * Transforms a given xml resource to a resource Object 083 */ 084 protected abstract Resource toResource(SMInputCursor resourceCursor) throws XMLStreamException; 085 086 protected abstract String messageFor(SMInputCursor violationCursor) throws XMLStreamException; 087 088 protected abstract String ruleKey(SMInputCursor violationCursor) throws XMLStreamException; 089 090 protected abstract String keyForPlugin(); 091 092 protected abstract String lineNumberForViolation(SMInputCursor violationCursor) throws XMLStreamException; 093 094 public void parse(File violationsXMLFile) throws XMLStreamException { 095 if (violationsXMLFile != null && violationsXMLFile.exists()) { 096 InputStream input = null; 097 try { 098 input = new FileInputStream(violationsXMLFile); 099 parse(input); 100 101 } catch (FileNotFoundException e) { 102 throw new XMLStreamException(e); 103 104 } finally { 105 IOUtils.closeQuietly(input); 106 } 107 } 108 } 109 110 public final void parse(InputStream input) throws XMLStreamException { 111 if (input != null) { 112 StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() { 113 public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException { 114 parseResources(rootCursor.advance()); 115 } 116 }, true); 117 parser.parse(input); 118 } 119 } 120 121 private void parseResources(SMInputCursor rootCursor) throws XMLStreamException { 122 SMInputCursor resourcesCursor = cursorForResources(rootCursor); 123 SMEvent event; 124 while ((event = resourcesCursor.getNext()) != null) { 125 if (event.compareTo(SMEvent.START_ELEMENT) == 0) { 126 parseViolations(resourcesCursor); 127 } 128 } 129 } 130 131 private void parseViolations(SMInputCursor resourcesCursor) throws XMLStreamException { 132 Resource resource = toResource(resourcesCursor); 133 SMInputCursor violationsCursor = cursorForViolations(resourcesCursor); 134 SMEvent event; 135 while ((event = violationsCursor.getNext()) != null) { 136 if (event.compareTo(SMEvent.START_ELEMENT) == 0) { 137 createViolationFor(resource, violationsCursor); 138 } 139 } 140 } 141 142 private void createViolationFor(Resource resource, SMInputCursor violationCursor) throws XMLStreamException { 143 Rule rule = getRule(violationCursor); 144 Integer line = getLineIndex(violationCursor); 145 if (rule != null && resource != null) { 146 Violation violation = new Violation(rule, resource) 147 .setLineId(line) 148 .setMessage(messageFor(violationCursor)); 149 context.saveViolation(violation); 150 } 151 } 152 153 private Rule getRule(SMInputCursor violationCursor) throws XMLStreamException { 154 return rulesManager.getPluginRule(keyForPlugin(), ruleKey(violationCursor)); 155 } 156 157 private Integer getLineIndex(SMInputCursor violationCursor) throws XMLStreamException { 158 String line = lineNumberForViolation(violationCursor); 159 return isNotBlank(line) ? parseLineIndex(line) : null; 160 } 161 162 private Integer parseLineIndex(String line) { 163 try { 164 return (int) ParsingUtils.parseNumber(line); 165 } catch (ParseException ignore) { 166 return null; 167 } 168 } 169 170 }