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 static org.apache.commons.lang.StringUtils.isNotBlank;
023
024 import java.io.File;
025 import java.io.FileInputStream;
026 import java.io.FileNotFoundException;
027 import java.io.InputStream;
028 import java.text.ParseException;
029
030 import javax.xml.stream.XMLStreamException;
031
032 import org.apache.commons.io.IOUtils;
033 import org.codehaus.staxmate.in.SMEvent;
034 import org.codehaus.staxmate.in.SMHierarchicCursor;
035 import org.codehaus.staxmate.in.SMInputCursor;
036 import org.sonar.api.profiles.RulesProfile;
037 import org.sonar.api.resources.Resource;
038 import org.sonar.api.rules.Rule;
039 import org.sonar.api.rules.RulesManager;
040 import org.sonar.api.rules.Violation;
041 import org.sonar.api.utils.ParsingUtils;
042 import org.sonar.api.utils.StaxParser;
043
044 /**
045 * @since 1.10
046 * @deprecated since 2.3. Too much "black-box".
047 */
048 @Deprecated
049 public abstract class AbstractViolationsStaxParser {
050
051 protected RulesManager rulesManager;
052 protected SensorContext context;
053 protected boolean doSaveViolationsOnUnexistedResource = true;
054
055 /**
056 * @deprecated since 1.11.
057 */
058 @Deprecated
059 protected AbstractViolationsStaxParser(SensorContext context, RulesManager rulesManager, RulesProfile profile) {
060 this.rulesManager = rulesManager;
061 this.context = context;
062 }
063
064 protected AbstractViolationsStaxParser(SensorContext context, RulesManager rulesManager) {
065 this.rulesManager = rulesManager;
066 this.context = context;
067 }
068
069 /**
070 * Cursor for child resources to parse, the returned input cursor should be filtered on SMEvent.START_ELEMENT for optimal perfs
071 *
072 * @param rootCursor
073 * the root xml doc cursor
074 * @return a cursor with child resources elements to parse
075 */
076 protected abstract SMInputCursor cursorForResources(SMInputCursor rootCursor) throws XMLStreamException;
077
078 /**
079 * Cursor for violations to parse for a given resource, the returned input cursor should be filtered on SMEvent.START_ELEMENT for optimal
080 * perfs
081 *
082 * @param resourcesCursor
083 * the current resource cursor
084 * @return a cursor with child violations elements to parse
085 */
086 protected abstract SMInputCursor cursorForViolations(SMInputCursor resourcesCursor) throws XMLStreamException;
087
088 /**
089 * Transforms a given xml resource to a resource Object
090 */
091 protected abstract Resource toResource(SMInputCursor resourceCursor) throws XMLStreamException;
092
093 protected abstract String messageFor(SMInputCursor violationCursor) throws XMLStreamException;
094
095 protected abstract String ruleKey(SMInputCursor violationCursor) throws XMLStreamException;
096
097 protected abstract String keyForPlugin();
098
099 protected abstract String lineNumberForViolation(SMInputCursor violationCursor) throws XMLStreamException;
100
101 /**
102 * Specify if violations must be saved even if when the Resource associated to a violation doesn't yet exist.
103 * In that case the Resource is automatically created.
104 *
105 * @param doSaveViolationsOnUnexistedResource by default, the value is true
106 */
107 public final void setDoSaveViolationsOnUnexistedResource(boolean doSaveViolationsOnUnexistedResource) {
108 this.doSaveViolationsOnUnexistedResource = doSaveViolationsOnUnexistedResource;
109 }
110
111 public void parse(File violationsXMLFile) throws XMLStreamException {
112 if (violationsXMLFile != null && violationsXMLFile.exists()) {
113 InputStream input = null;
114 try {
115 input = new FileInputStream(violationsXMLFile);
116 parse(input);
117
118 } catch (FileNotFoundException e) {
119 throw new XMLStreamException(e);
120
121 } finally {
122 IOUtils.closeQuietly(input);
123 }
124 }
125 }
126
127 public final void parse(InputStream input) throws XMLStreamException {
128 if (input != null) {
129 StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() {
130
131 public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
132 parseResources(rootCursor.advance());
133 }
134 }, true);
135 parser.parse(input);
136 }
137 }
138
139 private void parseResources(SMInputCursor rootCursor) throws XMLStreamException {
140 SMInputCursor resourcesCursor = cursorForResources(rootCursor);
141 SMEvent event;
142 while ((event = resourcesCursor.getNext()) != null) {
143 if (event.compareTo(SMEvent.START_ELEMENT) == 0) {
144 parseViolations(resourcesCursor);
145 }
146 }
147 }
148
149 private void parseViolations(SMInputCursor resourcesCursor) throws XMLStreamException {
150 Resource resource = toResource(resourcesCursor);
151 if ( !doSaveViolationsOnUnexistedResource && context.getResource(resource) == null) {
152 return;
153 }
154 SMInputCursor violationsCursor = cursorForViolations(resourcesCursor);
155 SMEvent event;
156 while ((event = violationsCursor.getNext()) != null) {
157 if (event.compareTo(SMEvent.START_ELEMENT) == 0) {
158 createViolationFor(resource, violationsCursor);
159 }
160 }
161 }
162
163 private void createViolationFor(Resource resource, SMInputCursor violationCursor) throws XMLStreamException {
164 Rule rule = getRule(violationCursor);
165 Integer line = getLineIndex(violationCursor);
166 if (rule != null && resource != null) {
167 Violation violation = Violation.create(rule, resource)
168 .setLineId(line)
169 .setMessage(messageFor(violationCursor));
170 context.saveViolation(violation);
171 }
172 }
173
174 private Rule getRule(SMInputCursor violationCursor) throws XMLStreamException {
175 return rulesManager.getPluginRule(keyForPlugin(), ruleKey(violationCursor));
176 }
177
178 private Integer getLineIndex(SMInputCursor violationCursor) throws XMLStreamException {
179 String line = lineNumberForViolation(violationCursor);
180 return parseLineIndex(line);
181 }
182
183 protected static Integer parseLineIndex(String line) {
184 if ( !isNotBlank(line) || line.indexOf('-') != -1) {
185 return null;
186 }
187 try {
188 return (int) ParsingUtils.parseNumber(line);
189 } catch (ParseException ignore) {
190 return null;
191 }
192 }
193
194 }