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.squid.text;
021
022 import java.util.Arrays;
023 import java.util.Collection;
024 import java.util.HashSet;
025 import java.util.Set;
026
027 import org.sonar.squid.measures.Metric;
028 import org.sonar.squid.recognizer.CodeRecognizer;
029
030 import com.puppycrawl.tools.checkstyle.api.TextBlock;
031
032 public class Source {
033
034 private Line[] lines;
035 private CodeRecognizer codeRecognizer;
036 private Set<Integer> noSonarTagLines = new HashSet<Integer>();
037
038 public Source(String[] stringLines, CodeRecognizer codeRecognizer, TextBlock... comments) {
039 this(stringLines, codeRecognizer, Arrays.asList(comments));
040 }
041
042 public Source(String[] stringLines, CodeRecognizer codeRecognizer, Collection<TextBlock> comments) {
043 lines = new Line[stringLines.length];
044 this.codeRecognizer = codeRecognizer;
045 int index = 0;
046 for (String stringLine : stringLines) {
047 lines[index++] = new Line(stringLine);
048 }
049 attachTextBlockToLine(comments);
050 for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) {
051 computeBlankLine(lines[lineIndex]);
052 computeHeaderCommentLine(lines[lineIndex]);
053 computeCommentLine(lines[lineIndex]);
054 computeCommentBlankLine(lines[lineIndex]);
055 computeLineOfCode(lines[lineIndex]);
056 computeNoSonarTag(lines[lineIndex], lineIndex + 1);
057 }
058 }
059
060 private void computeNoSonarTag(Line line, int lineIndex) {
061 if (line.isThereNoSonarTag()) {
062 noSonarTagLines.add(lineIndex);
063 }
064 }
065
066 private void computeLineOfCode(Line line) {
067 if (line.isThereCode()) {
068 line.setMeasure(Metric.LINES_OF_CODE, 1);
069 }
070 }
071
072 private void computeHeaderCommentLine(Line line) {
073 if (line.isThereComment() && !line.isThereBlankComment() && line.isThereLicenseHeaderComment()) {
074 line.setMeasure(Metric.HEADER_COMMENT_LINES, 1);
075 }
076 }
077
078 private void computeCommentLine(Line line) {
079 if (line.isThereComment() && !line.isThereBlankComment()) {
080 if (line.isThereJavadoc() || line.isThereLicenseHeaderComment()) {
081 line.setMeasure(Metric.COMMENT_LINES, 1);
082 return;
083 }
084
085 boolean isCommentedOutCode = codeRecognizer.isLineOfCode(line.getComment());
086 if (!isCommentedOutCode) {
087 line.setMeasure(Metric.COMMENT_LINES, 1);
088 } else {
089 line.setMeasure(Metric.COMMENTED_OUT_CODE_LINES, 1);
090 }
091 }
092 }
093
094 private void computeBlankLine(Line line) {
095 if (line.isBlank()) {
096 line.setMeasure(Metric.BLANK_LINES, 1);
097 }
098 }
099
100 private void computeCommentBlankLine(Line line) {
101 if (line.isThereBlankComment()) {
102 line.setMeasure(Metric.COMMENT_BLANK_LINES, 1);
103 }
104 }
105
106 private void attachTextBlockToLine(Collection<TextBlock> comments) {
107 for (TextBlock comment : comments) {
108 boolean isJavadoc = comment.getText()[0].startsWith("/**");
109 boolean isLicenseHeader = (comment.getStartLineNo() == 1);
110 for (int index = comment.getStartLineNo(); index <= comment.getEndLineNo(); index++) {
111 lines[index - 1].setComment(comment.getText()[index - comment.getStartLineNo()], isJavadoc, isLicenseHeader);
112 }
113 }
114 }
115
116 public int getMeasure(Metric metric) {
117 return getMeasure(metric, 1, lines.length);
118 }
119
120 public int getMeasure(Metric metric, int fromLine, int toLine) {
121 if (toLine > lines.length) {
122 throw new IllegalStateException("There are only " + lines.length + " lines in the file and you're trying to reach line " + toLine);
123 }
124 if (fromLine < 1) {
125 throw new IllegalStateException("Line index starts from 1 and not from " + fromLine);
126 }
127
128 int measure = 0;
129 for (int index = fromLine; index < toLine + 1; index++) {
130 measure += lines[index - 1].getInt(metric);
131 }
132 return measure;
133 }
134
135 public Set<Integer> getNoSonarTagLines() {
136 return noSonarTagLines;
137 }
138 }