001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
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 */
020package org.sonar.squid.text;
021
022import com.google.common.annotations.Beta;
023import org.sonar.squid.measures.Metric;
024import org.sonar.squid.recognizer.CodeRecognizer;
025
026import java.io.Reader;
027import java.util.ArrayList;
028import java.util.HashSet;
029import java.util.List;
030import java.util.Set;
031
032public class Source {
033
034  private List<Line> lines = new ArrayList<Line>();
035  private CodeRecognizer codeRecognizer;
036  private Set<Integer> noSonarTagLines = new HashSet<Integer>();
037
038  public Source(Reader reader, CodeRecognizer codeRecognizer, String... additionalSingleLineCommentFlag) {
039    this.codeRecognizer = codeRecognizer;
040    LinesFactory linesFactory = new LinesFactory(reader, additionalSingleLineCommentFlag);
041    lines = linesFactory.getLines();
042    processLines();
043  }
044
045  public Source(String[] stringLines, CodeRecognizer codeRecognizer) {
046    this(new StringArrayReader(stringLines), codeRecognizer);
047  }
048
049  private void processLines() {
050    for (Line line : lines) {
051      computeBlankLine(line);
052      computeHeaderCommentLine(line);
053      computeCommentLine(line);
054      computeCommentBlankLine(line);
055      computeLineOfCode(line);
056      computeNoSonarTag(line);
057      line.deleteLineContent();
058    }
059  }
060
061  private void computeNoSonarTag(Line line) {
062    if (line.isThereNoSonarTag()) {
063      noSonarTagLines.add(line.getLineIndex());
064    }
065  }
066
067  private void computeLineOfCode(Line line) {
068    if (line.isThereCode()) {
069      line.setMeasure(Metric.LINES_OF_CODE, 1);
070    }
071  }
072
073  private void computeHeaderCommentLine(Line line) {
074    if (line.isThereComment() && !line.isThereBlankComment() && line.isThereLicenseHeaderComment()) {
075      line.setMeasure(Metric.HEADER_COMMENT_LINES, 1);
076    }
077  }
078
079  private void computeCommentLine(Line line) {
080    if (line.isThereComment() && !line.isThereBlankComment()) {
081      if (line.isThereJavadoc() || line.isThereLicenseHeaderComment()) {
082        line.setMeasure(Metric.COMMENT_LINES, 1);
083        return;
084      }
085
086      boolean isCommentedOutCode = codeRecognizer.isLineOfCode(line.getComment());
087      if (!isCommentedOutCode) {
088        line.setMeasure(Metric.COMMENT_LINES, 1);
089      } else {
090        line.setMeasure(Metric.COMMENTED_OUT_CODE_LINES, 1);
091      }
092    }
093  }
094
095  private void computeBlankLine(Line line) {
096    if (line.isBlank()) {
097      line.setMeasure(Metric.BLANK_LINES, 1);
098    }
099  }
100
101  private void computeCommentBlankLine(Line line) {
102    if (line.isThereBlankComment()) {
103      line.setMeasure(Metric.COMMENT_BLANK_LINES, 1);
104    }
105  }
106
107  public int getMeasure(Metric metric) {
108    return getMeasure(metric, 1, lines.size());
109  }
110
111  /**
112   * Numbering of lines starts from 1.
113   */
114  public int getMeasure(Metric metric, int fromLine, int toLine) {
115    if (toLine > lines.size()) {
116      throw new IllegalStateException("There are only " + lines.size() + " lines in the file and you're trying to reach line " + toLine);
117    }
118    if (fromLine < 1) {
119      throw new IllegalStateException("Line index starts from 1 and not from " + fromLine);
120    }
121
122    int measure = 0;
123    for (int index = fromLine; index < toLine + 1; index++) {
124      measure += lines.get(index - 1).getInt(metric);
125    }
126    return measure;
127  }
128
129  public Set<Integer> getNoSonarTagLines() {
130    return noSonarTagLines;
131  }
132
133  /**
134   * @since 2.14
135   */
136  @Beta
137  public int getNumberOfLines() {
138    return lines.size();
139  }
140
141}