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 }
102 catch (FileNotFoundException e) {
103 throw new XMLStreamException(e);
104
105 }
106 finally {
107 IOUtils.closeQuietly(input);
108 }
109 }
110 }
111
112 public final void parse(InputStream input) throws XMLStreamException {
113 if (input != null) {
114 StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() {
115 public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
116 parseResources(rootCursor.advance());
117 }
118 }, true);
119 parser.parse(input);
120 }
121 }
122
123 private void parseResources(SMInputCursor rootCursor) throws XMLStreamException {
124 SMInputCursor resourcesCursor = cursorForResources(rootCursor);
125 SMEvent event;
126 while ((event = resourcesCursor.getNext()) != null) {
127 if (event.compareTo(SMEvent.START_ELEMENT) == 0) {
128 parseViolations(resourcesCursor);
129 }
130 }
131 }
132
133 private void parseViolations(SMInputCursor resourcesCursor) throws XMLStreamException {
134 Resource resource = toResource(resourcesCursor);
135 SMInputCursor violationsCursor = cursorForViolations(resourcesCursor);
136 SMEvent event;
137 while ((event = violationsCursor.getNext()) != null) {
138 if (event.compareTo(SMEvent.START_ELEMENT) == 0) {
139 createViolationFor(resource, violationsCursor);
140 }
141 }
142 }
143
144 private void createViolationFor(Resource resource, SMInputCursor violationCursor) throws XMLStreamException {
145 Rule rule = getRule(violationCursor);
146 Integer line = getLineIndex(violationCursor);
147 if (rule != null && resource != null) {
148 Violation violation = new Violation(rule, resource)
149 .setLineId(line)
150 .setMessage(messageFor(violationCursor));
151 context.saveViolation(violation);
152 }
153 }
154
155 private Rule getRule(SMInputCursor violationCursor) throws XMLStreamException {
156 return rulesManager.getPluginRule(keyForPlugin(), ruleKey(violationCursor));
157 }
158
159 private Integer getLineIndex(SMInputCursor violationCursor) throws XMLStreamException {
160 String line = lineNumberForViolation(violationCursor);
161 return parseLineIndex(line);
162 }
163
164 protected Integer parseLineIndex(String line) {
165 if (!isNotBlank(line) || line.indexOf('-') != -1) {
166 return null;
167 }
168 try {
169 return (int) ParsingUtils.parseNumber(line);
170 }
171 catch (ParseException ignore) {
172 return null;
173 }
174 }
175
176 }