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 final 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 }