001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube 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     * SonarQube 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 License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.xoo.lang;
021    
022    import com.google.common.annotations.VisibleForTesting;
023    import com.google.common.base.Charsets;
024    import org.apache.commons.io.FileUtils;
025    import org.apache.commons.lang.StringUtils;
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    import org.sonar.api.batch.fs.FileSystem;
029    import org.sonar.api.batch.fs.InputFile;
030    import org.sonar.api.batch.sensor.Sensor;
031    import org.sonar.api.batch.sensor.SensorContext;
032    import org.sonar.api.batch.sensor.SensorDescriptor;
033    import org.sonar.api.measures.CoreMetrics;
034    import org.sonar.api.measures.FileLinesContext;
035    import org.sonar.api.measures.FileLinesContextFactory;
036    import org.sonar.api.utils.DateUtils;
037    import org.sonar.xoo.Xoo;
038    
039    import java.io.File;
040    import java.io.IOException;
041    import java.util.Date;
042    import java.util.List;
043    
044    public class ScmActivitySensor implements Sensor {
045    
046      private static final Logger LOG = LoggerFactory.getLogger(ScmActivitySensor.class);
047    
048      private static final String SCM_EXTENSION = ".scm";
049    
050      private final FileSystem fs;
051      private final FileLinesContextFactory fileLinesContextFactory;
052    
053      public ScmActivitySensor(FileLinesContextFactory fileLinesContextFactory, FileSystem fileSystem) {
054        this.fs = fileSystem;
055        this.fileLinesContextFactory = fileLinesContextFactory;
056      }
057    
058      @Override
059      public void describe(SensorDescriptor descriptor) {
060        descriptor
061          .name(this.getClass().getSimpleName())
062          .provides(CoreMetrics.SCM_AUTHORS_BY_LINE,
063            CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE,
064            CoreMetrics.SCM_REVISIONS_BY_LINE)
065          .workOnLanguages(Xoo.KEY);
066      }
067    
068      @Override
069      public void execute(SensorContext context) {
070        for (InputFile inputFile : fs.inputFiles(fs.predicates().hasLanguage(Xoo.KEY))) {
071          processFile(inputFile);
072        }
073    
074      }
075    
076      @VisibleForTesting
077      protected void processFile(InputFile inputFile) {
078        File ioFile = inputFile.file();
079        File scmDataFile = new java.io.File(ioFile.getParentFile(), ioFile.getName() + SCM_EXTENSION);
080        if (!scmDataFile.exists()) {
081          LOG.debug("Skipping SCM data injection for " + inputFile.relativePath());
082          return;
083        }
084    
085        FileLinesContext fileLinesContext = fileLinesContextFactory.createFor(inputFile);
086        try {
087          List<String> lines = FileUtils.readLines(scmDataFile, Charsets.UTF_8.name());
088          int lineNumber = 0;
089          for (String line : lines) {
090            lineNumber++;
091            if (StringUtils.isNotBlank(line)) {
092              // revision,author,dateTime
093              String[] fields = StringUtils.split(line, ',');
094              if (fields.length < 3) {
095                throw new IllegalStateException("Not enough fields on line " + lineNumber);
096              }
097              String revision = fields[0];
098              String author = fields[1];
099              // Will throw an exception, when date is not in format "yyyy-MM-dd"
100              Date date = DateUtils.parseDate(fields[2]);
101    
102              fileLinesContext.setStringValue(CoreMetrics.SCM_REVISIONS_BY_LINE_KEY, lineNumber, revision);
103              fileLinesContext.setStringValue(CoreMetrics.SCM_AUTHORS_BY_LINE_KEY, lineNumber, author);
104              fileLinesContext.setStringValue(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY, lineNumber, DateUtils.formatDateTime(date));
105            }
106          }
107        } catch (IOException e) {
108          throw new IllegalStateException(e);
109        }
110        fileLinesContext.save();
111      }
112    }