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