org.sonar.server.source.index.SourceLineResultSetIterator.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.server.source.index.SourceLineResultSetIterator.java

Source

/*
 * SonarQube, open source software quality management tool.
 * Copyright (C) 2008-2014 SonarSource
 * mailto:contact AT sonarsource DOT com
 *
 * SonarQube is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * SonarQube is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.server.source.index;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.sonar.api.utils.DateUtils;
import org.sonar.server.db.DbClient;
import org.sonar.server.db.ResultSetIterator;
import org.sonar.server.db.migrations.SqlUtil;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Scroll over table FILE_SOURCES and directly parse CSV field required to
 * populate the index sourcelines
 */
public class SourceLineResultSetIterator extends ResultSetIterator<SourceLineResultSetIterator.SourceFile> {

    public static class SourceFile {
        private final String fileUuid;
        private final long updatedAt;
        private final List<SourceLineDoc> lines = Lists.newArrayList();

        public SourceFile(String fileUuid, long updatedAt) {
            this.fileUuid = fileUuid;
            this.updatedAt = updatedAt;
        }

        public String getFileUuid() {
            return fileUuid;
        }

        public long getUpdatedAt() {
            return updatedAt;
        }

        public List<SourceLineDoc> getLines() {
            return lines;
        }

        public void addLine(SourceLineDoc line) {
            this.lines.add(line);
        }
    }

    private static final String[] FIELDS = {
            // column 1
            "project_uuid", "file_uuid", "updated_at", "data" };

    private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from file_sources";

    private static final String SQL_AFTER_DATE = SQL_ALL + " where updated_at>?";

    public static SourceLineResultSetIterator create(DbClient dbClient, Connection connection, long afterDate) {
        try {
            String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL;
            // rows are big, so they are scrolled once at a time (one row in memory at a time)
            PreparedStatement stmt = dbClient.newScrollingSingleRowSelectStatement(connection, sql);
            if (afterDate > 0L) {
                stmt.setLong(1, afterDate);
            }
            return new SourceLineResultSetIterator(stmt);
        } catch (SQLException e) {
            throw new IllegalStateException("Fail to prepare SQL request to select all file sources", e);
        }
    }

    private SourceLineResultSetIterator(PreparedStatement stmt) throws SQLException {
        super(stmt);
    }

    @Override
    protected SourceFile read(ResultSet rs) throws SQLException {
        String projectUuid = rs.getString(1);
        String fileUuid = rs.getString(2);
        Long updatedAt = SqlUtil.getLong(rs, 3);
        if (updatedAt == null) {
            updatedAt = System.currentTimeMillis();
        }
        Date updatedDate = new Date(updatedAt);
        SourceFile result = new SourceFile(fileUuid, updatedAt);

        Reader csv = rs.getCharacterStream(4);
        if (csv == null) {
            return result;
        }

        int line = 1;
        CSVParser csvParser = null;
        try {
            csvParser = new CSVParser(csv, CSVFormat.DEFAULT);

            for (CSVRecord csvRecord : csvParser) {
                SourceLineDoc doc = new SourceLineDoc(Maps.<String, Object>newHashMap());

                doc.setProjectUuid(projectUuid);
                doc.setFileUuid(fileUuid);
                doc.setLine(line);
                doc.setUpdateDate(updatedDate);
                doc.setScmRevision(csvRecord.get(0));
                doc.setScmAuthor(csvRecord.get(1));
                doc.setScmDate(DateUtils.parseDateTimeQuietly(csvRecord.get(2)));
                // UT
                doc.setUtLineHits(parseIntegerFromRecord(csvRecord.get(3)));
                doc.setUtConditions(parseIntegerFromRecord(csvRecord.get(4)));
                doc.setUtCoveredConditions(parseIntegerFromRecord(csvRecord.get(5)));
                // IT
                doc.setItLineHits(parseIntegerFromRecord(csvRecord.get(6)));
                doc.setItConditions(parseIntegerFromRecord(csvRecord.get(7)));
                doc.setItCoveredConditions(parseIntegerFromRecord(csvRecord.get(8)));
                // OVERALL
                doc.setOverallLineHits(parseIntegerFromRecord(csvRecord.get(9)));
                doc.setOverallConditions(parseIntegerFromRecord(csvRecord.get(10)));
                doc.setOverallCoveredConditions(parseIntegerFromRecord(csvRecord.get(11)));
                doc.setHighlighting(csvRecord.get(12));
                doc.setSymbols(csvRecord.get(13));

                doc.setDuplications(parseDuplications(csvRecord.get(14)));
                doc.setSource(csvRecord.get(csvRecord.size() - 1));

                result.addLine(doc);

                line++;
            }
        } catch (IOException ioError) {
            throw new IllegalStateException(
                    "Impossible to open stream for file_sources.data with file_uuid " + fileUuid, ioError);
        } catch (ArrayIndexOutOfBoundsException lineError) {
            throw new IllegalStateException(
                    String.format("Impossible to parse source line data, stuck at line %d", line), lineError);
        } finally {
            IOUtils.closeQuietly(csv);
            IOUtils.closeQuietly(csvParser);
        }

        return result;
    }

    private List<Integer> parseDuplications(@Nullable String duplications) {
        List<Integer> dups = Lists.newArrayList();
        if (StringUtils.isNotEmpty(duplications)) {
            StringTokenizer tokenizer = new StringTokenizer(duplications, ",", false);
            while (tokenizer.hasMoreTokens()) {
                dups.add(NumberUtils.toInt(tokenizer.nextToken(), -1));
            }
        }
        return dups;
    }

    @CheckForNull
    private Integer parseIntegerFromRecord(@Nullable String cellValue) {
        if (cellValue == null || cellValue.isEmpty()) {
            return null;
        } else {
            return Integer.parseInt(cellValue);
        }
    }
}