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    }