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 }