aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-squid/src/main/java/org/sonar/squid/text
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
committersimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
commitaeadc1f9129274949daaa57738c7c4550bdfbc7b (patch)
tree08dadf5ef7474fc41d1d48f74648f1ba8b55f34d /sonar-squid/src/main/java/org/sonar/squid/text
downloadsonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.tar.gz
sonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.zip
SONAR-236 remove deprecated code from checkstyle plugin + display default value of rule parameters in Q profile console
Diffstat (limited to 'sonar-squid/src/main/java/org/sonar/squid/text')
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/Line.java233
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/LineContextHandler.java59
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/LinesFactory.java139
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/LiteralValueHandler.java59
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/MultiLinesCommentHandler.java101
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/SingleLineCommentHandler.java63
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/Source.java128
-rw-r--r--sonar-squid/src/main/java/org/sonar/squid/text/StringArrayReader.java96
8 files changed, 878 insertions, 0 deletions
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/Line.java b/sonar-squid/src/main/java/org/sonar/squid/text/Line.java
new file mode 100644
index 00000000000..ef633b4c79f
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/Line.java
@@ -0,0 +1,233 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.squid.measures.Measurable;
+import org.sonar.squid.measures.Metric;
+
+class Line implements Measurable<Metric> {
+
+ private final int lineIndex;
+ private int blankLine = 0;
+ private int line = 1;
+ private int lineOfCode = 0;
+ private int commentLine = 0;
+ private int headerCommentLine = 0;
+ private int commentBlankLine = 0;
+ private int commentedOutCodeLine = 0;
+ private String comment = null;
+ private StringBuilder stringLine;
+ private boolean isBlank;
+ private boolean isThereJavadoc;
+ private boolean isThereLicenseHeaderComment;
+ private final static String NOSONAR_TAG = "NOSONAR";
+
+ Line() {
+ this.lineIndex = 0;
+ }
+
+ Line(String stringLine) {
+ this();
+ setString(new StringBuilder(stringLine));
+ }
+
+ Line(int lineIndex, StringBuilder stringLine) {
+ this(lineIndex);
+ setString(stringLine);
+ }
+
+ Line(int lineIndex) {
+ this.lineIndex = lineIndex;
+ }
+
+ final void setString(StringBuilder stringLine) {
+ this.stringLine = stringLine;
+ isBlank = isBlankLine();
+ }
+
+ private boolean isBlankLine() {
+ for (int i = 0; i < stringLine.length(); i++) {
+ if ( !Character.isWhitespace(stringLine.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double getDouble(Metric metric) {
+ return getInt(metric);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getInt(Metric metric) {
+ switch (metric) {
+ case BLANK_LINES:
+ return blankLine;
+ case LINES:
+ return line;
+ case LINES_OF_CODE:
+ return lineOfCode;
+ case COMMENT_LINES:
+ return commentLine;
+ case COMMENTED_OUT_CODE_LINES:
+ return commentedOutCodeLine;
+ case COMMENT_BLANK_LINES:
+ return commentBlankLine;
+ case HEADER_COMMENT_LINES:
+ return headerCommentLine;
+ default:
+ throw new IllegalStateException("Metric " + metric.name() + " is not available on Line object.");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMeasure(Metric metric, double measure) {
+ setMeasure(metric, (int) measure);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMeasure(Metric metric, int measure) {
+ switch (metric) {
+ case BLANK_LINES:
+ blankLine = measure;
+ break;
+ case LINES_OF_CODE:
+ lineOfCode = measure;
+ break;
+ case COMMENT_LINES:
+ commentLine = measure;
+ break;
+ case COMMENTED_OUT_CODE_LINES:
+ commentedOutCodeLine = measure;
+ break;
+ case COMMENT_BLANK_LINES:
+ commentBlankLine = measure;
+ break;
+ case HEADER_COMMENT_LINES:
+ headerCommentLine = measure;
+ break;
+ case LINES:
+ throw new IllegalStateException("Metric LINES always equals 1 on a Line and you are not permitted to change this value.");
+ default:
+ throw new IllegalStateException("Metric " + metric.name() + " is not suitable for Line object.");
+ }
+ }
+
+ void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ void setComment(String comment, boolean isJavadoc) {
+ setComment(comment);
+ this.isThereJavadoc = isJavadoc;
+ }
+
+ void setComment(String comment, boolean isJavadoc, boolean isLicenseHeader) {
+ setComment(comment, isJavadoc);
+ this.isThereLicenseHeaderComment = isLicenseHeader;
+ }
+
+ String getString() {
+ return stringLine.toString();
+ }
+
+ boolean isBlank() {
+ return !isThereComment() && isBlank;
+ }
+
+ boolean isThereCode() {
+ if ( !isBlank() && !isThereComment()) {
+ return true;
+ }
+ if (isThereComment() && isThereCodeBeforeOrAfterComment()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isThereCodeBeforeOrAfterComment() {
+ if ( !isThereComment()) {
+ throw new IllegalStateException("You can't call this method when there isn't any comment");
+ }
+ boolean isThereCodeBeforeComment = false;
+ boolean isThereCodeAfterComment = false;
+ int commentStartIndex = stringLine.indexOf(comment);
+ int commentEndIndex = commentStartIndex + comment.length() - 1;
+ if (commentStartIndex > 0) {
+ isThereCodeBeforeComment = !StringUtils.isBlank(stringLine.substring(0, commentStartIndex - 1));
+ }
+ if (commentEndIndex > 0 && commentEndIndex != stringLine.length() - 1) {
+ isThereCodeAfterComment = !StringUtils.isBlank(stringLine.substring(commentEndIndex + 1));
+ }
+ return isThereCodeBeforeComment || isThereCodeAfterComment;
+ }
+
+ boolean isThereComment() {
+ return comment != null;
+ }
+
+ String getComment() {
+ return comment;
+ }
+
+ boolean isThereBlankComment() {
+ if (isThereComment()) {
+ for (int i = 0; i < comment.length(); i++) {
+ char character = comment.charAt(i);
+ if ( !Character.isWhitespace(character) && character != '*' && character != '/') {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ boolean isThereJavadoc() {
+ return isThereJavadoc;
+ }
+
+ boolean isThereLicenseHeaderComment() {
+ return isThereLicenseHeaderComment;
+ }
+
+ boolean isThereNoSonarTag() {
+ return isThereComment() && comment.contains(NOSONAR_TAG);
+ }
+
+ int getLineIndex() {
+ return lineIndex;
+ }
+
+ void deleteLineContent() {
+ comment = null;
+ stringLine = null;
+ }
+}
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/LineContextHandler.java b/sonar-squid/src/main/java/org/sonar/squid/text/LineContextHandler.java
new file mode 100644
index 00000000000..097478c9f85
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/LineContextHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+abstract class LineContextHandler {
+
+ abstract boolean matchToEnd(Line line, StringBuilder pendingLine);
+
+ abstract boolean matchWithEndOfLine(Line line, StringBuilder pendingLine);
+
+ abstract boolean matchToBegin(Line line, StringBuilder pendingLine);
+
+ static boolean matchEndOfString(StringBuilder pendingLine, String end) {
+ int pendingLineIndex = pendingLine.length() - end.length();
+ if (pendingLineIndex < 0) {
+ return false;
+ }
+ for (int endIndex = 0; endIndex < end.length(); endIndex++) {
+ char endChar = end.charAt(endIndex);
+ char pendingLineChar = pendingLine.charAt(pendingLineIndex + endIndex);
+ if (endChar != pendingLineChar) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static boolean matchEndOfString(StringBuilder pendingLine, char endChar) {
+ if (pendingLine.length() < 1) {
+ return false;
+ }
+ return pendingLine.charAt(pendingLine.length() - 1) == endChar;
+ }
+
+ static char getLastCharacter(StringBuilder pendingLine) {
+ if (pendingLine.length() < 1) {
+ throw new IllegalStateException("The pending line is empty.");
+ }
+ return pendingLine.charAt(pendingLine.length() - 1);
+ }
+
+}
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/LinesFactory.java b/sonar-squid/src/main/java/org/sonar/squid/text/LinesFactory.java
new file mode 100644
index 00000000000..6e98de3da35
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/LinesFactory.java
@@ -0,0 +1,139 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.sonar.squid.api.AnalysisException;
+
+class LinesFactory {
+
+ private final List<Line> lines = new ArrayList<Line>();
+ private char lastReadCharacter;
+ private StringBuilder currentStringBuilder = new StringBuilder();
+ private Line currentLine;
+ private static final char LF = '\n';
+ private static final char CR = '\r';
+ private static final int EOF = -1;
+ private LineContextHandler currentHandler;
+ private LineContextHandler[] handlers;
+
+ LinesFactory(Reader reader, String... additionalSingleLineCommentFlags) {
+ List<LineContextHandler> tmpHandlers = new ArrayList<LineContextHandler>();
+ for (String additionalSingleLineCommentFlag : additionalSingleLineCommentFlags) {
+ tmpHandlers.add(new SingleLineCommentHandler(additionalSingleLineCommentFlag));
+ }
+ tmpHandlers.add(new SingleLineCommentHandler("//", "*//"));
+ tmpHandlers.add(new MultiLinesCommentHandler());
+ tmpHandlers.add(new LiteralValueHandler('\''));
+ tmpHandlers.add(new LiteralValueHandler('"'));
+ this.handlers = tmpHandlers.toArray(new LineContextHandler[tmpHandlers.size()]);
+ fillLines(new BufferedReader(reader));
+ }
+
+ private void fillLines(Reader reader) {
+ try {
+ currentLine = new Line(1);
+ int nextChar;
+ do {
+ nextChar = reader.read();
+ if (isEndOfFile(nextChar)) {
+ notifyHandlersAboutEndOfLine();
+ break;
+ }
+ lastReadCharacter = (char) nextChar;
+ if (isEndOfLine(nextChar)) {
+ popOptionalRemainingEndOfLineChar(reader);
+ createNewLine();
+ continue;
+ }
+ appendToStringBuilder(nextChar);
+ notifyHandlersAboutNewChar();
+ } while (true);
+ } catch (IOException e) {
+ throw new AnalysisException("Unable to read the source code.", e);
+ } catch (Exception e) {
+ throw new AnalysisException("A problem was encountered when analyzing line " + lines.size() + " : '"
+ + currentStringBuilder.toString() + "'", e);
+ }
+ }
+
+ private void popOptionalRemainingEndOfLineChar(Reader reader) throws IOException {
+ reader.mark(1);
+ char nextChar = (char) reader.read();
+ reader.reset();
+ if (isTechnicalCharacter(nextChar) && lastReadCharacter != nextChar) {
+ reader.read();
+ }
+ }
+
+ private void notifyHandlersAboutNewChar() {
+ if (currentHandler == null) {
+ for (LineContextHandler handler : handlers) {
+ if (handler.matchToBegin(currentLine, currentStringBuilder)) {
+ currentHandler = handler;
+ break;
+ }
+ }
+ } else if (currentHandler.matchToEnd(currentLine, currentStringBuilder)) {
+ currentHandler = null;
+ }
+ }
+
+ private void notifyHandlersAboutEndOfLine() {
+ if (currentHandler != null && currentHandler.matchWithEndOfLine(currentLine, currentStringBuilder)) {
+ currentHandler = null;
+ }
+ }
+
+ private void createNewLine() {
+ notifyHandlersAboutEndOfLine();
+ currentLine.setString(currentStringBuilder);
+ lines.add(currentLine);
+ currentLine = new Line(lines.size() + 1);
+ currentStringBuilder = new StringBuilder();
+ }
+
+ private void appendToStringBuilder(int nextChar) {
+ if (!isTechnicalCharacter(nextChar)) {
+ currentStringBuilder.append((char) nextChar);
+ }
+ }
+
+ private boolean isEndOfFile(int nextChar) {
+ return nextChar == EOF && currentStringBuilder.length() == 0 && lastReadCharacter != LF;
+ }
+
+ private boolean isEndOfLine(int nextChar) {
+ return nextChar == EOF || (char) nextChar == LF || (char) nextChar == CR;
+ }
+
+ private boolean isTechnicalCharacter(int nextChar) {
+ return nextChar == LF || nextChar == CR || nextChar == EOF;
+ }
+
+ List<Line> getLines() {
+ return lines;
+ }
+}
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/LiteralValueHandler.java b/sonar-squid/src/main/java/org/sonar/squid/text/LiteralValueHandler.java
new file mode 100644
index 00000000000..907c87827c9
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/LiteralValueHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+public class LiteralValueHandler extends LineContextHandler {
+
+ private final char delimiter;
+
+ public LiteralValueHandler(char delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ @Override
+ boolean matchToEnd(Line line, StringBuilder pendingLine) {
+ return matchEndOfString(pendingLine, delimiter) && evenNumberOfBackSlashBeforeDelimiter(pendingLine);
+ }
+
+ private boolean evenNumberOfBackSlashBeforeDelimiter(StringBuilder pendingLine) {
+ int numberOfBackSlashChar = 0;
+ for (int index = pendingLine.length() - 2; index >= 0; index--) {
+ if (pendingLine.charAt(index) == '\\') {
+ numberOfBackSlashChar++;
+ } else {
+ break;
+ }
+ }
+ return numberOfBackSlashChar % 2 == 0;
+ }
+
+ @Override
+ boolean matchToBegin(Line line, StringBuilder pendingLine) {
+ if (matchEndOfString(pendingLine, delimiter)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ boolean matchWithEndOfLine(Line line, StringBuilder pendingLine) {
+ return true; //see http://jira.codehaus.org/browse/SONAR-1555
+ }
+}
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/MultiLinesCommentHandler.java b/sonar-squid/src/main/java/org/sonar/squid/text/MultiLinesCommentHandler.java
new file mode 100644
index 00000000000..5aeaadd12ce
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/MultiLinesCommentHandler.java
@@ -0,0 +1,101 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+public class MultiLinesCommentHandler extends LineContextHandler {
+
+ private StringBuilder currentLineComment;
+ private boolean isFirstLineOfComment = false;
+
+ private boolean isJavadoc = false;
+ private boolean isLicenseHeader = false;
+
+ private boolean commentStarted = false;
+
+ private static final String START_COMMENT_TAG = "/*";
+ private static final String START_JAVADOC_TAG = "/**";
+ private static final String START_GWT_NATIVE_CODE_TAG = "/*-{";
+ private static final String END_COMMENT_TAG = "*/";
+
+ @Override
+ boolean matchToEnd(Line line, StringBuilder pendingLine) {
+ if ( !commentStarted) {
+ throw new IllegalStateException("Method doContextBegin(StringBuilder pendingLine) has not been called first (line = '" + pendingLine
+ + "').");
+ }
+ currentLineComment.append(getLastCharacter(pendingLine));
+ if (isJavaDoc()) {
+ isJavadoc = true;
+ }
+ if (isGwtNativeCode()) {
+ initProperties();
+ return true;
+ }
+ boolean match = matchEndOfString(pendingLine, END_COMMENT_TAG);
+ if (match && !(isFirstLineOfComment && pendingLine.indexOf(START_COMMENT_TAG) + 1 == pendingLine.indexOf(END_COMMENT_TAG))) {
+ endOfCommentLine(line);
+ initProperties();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isGwtNativeCode() {
+ return isFirstLineOfComment && currentLineComment.length() == START_GWT_NATIVE_CODE_TAG.length()
+ && currentLineComment.toString().equals(START_GWT_NATIVE_CODE_TAG);
+ }
+
+ private boolean isJavaDoc() {
+ return isFirstLineOfComment && currentLineComment.length() == START_JAVADOC_TAG.length()
+ && currentLineComment.toString().equals(START_JAVADOC_TAG);
+ }
+
+ @Override
+ boolean matchToBegin(Line line, StringBuilder pendingLine) {
+ boolean match = matchEndOfString(pendingLine, START_COMMENT_TAG);
+ if (match) {
+ isFirstLineOfComment = true;
+ commentStarted = true;
+ currentLineComment = new StringBuilder(START_COMMENT_TAG);
+ isLicenseHeader = (line.getLineIndex() == 1);
+ }
+ return match;
+ }
+
+ @Override
+ boolean matchWithEndOfLine(Line line, StringBuilder pendingLine) {
+ endOfCommentLine(line);
+ return false;
+ }
+
+ private void endOfCommentLine(Line line) {
+ line.setComment(currentLineComment.toString(), isJavadoc, isLicenseHeader);
+ currentLineComment = new StringBuilder();
+ isFirstLineOfComment = false;
+ }
+
+ private void initProperties() {
+ commentStarted = false;
+ isJavadoc = false;
+ isLicenseHeader = false;
+ currentLineComment = new StringBuilder();
+ isFirstLineOfComment = false;
+ }
+}
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/SingleLineCommentHandler.java b/sonar-squid/src/main/java/org/sonar/squid/text/SingleLineCommentHandler.java
new file mode 100644
index 00000000000..f20861a96d8
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/SingleLineCommentHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+public class SingleLineCommentHandler extends LineContextHandler {
+
+ private StringBuilder comment;
+
+ private final String commentStartTag;
+ private final String commentNotStartTag;
+
+ public SingleLineCommentHandler(String commentStartTag) {
+ this(commentStartTag, null);
+ }
+
+ public SingleLineCommentHandler(String commentStartTag, String commentNotStartTag) {
+ this.commentStartTag = commentStartTag;
+ this.commentNotStartTag = commentNotStartTag;
+ }
+
+ @Override
+ boolean matchToEnd(Line line, StringBuilder pendingLine) {
+ if (comment == null) {
+ throw new IllegalStateException("Method doContextBegin(StringBuilder pendingLine) has not been called.");
+ }
+ comment.append(getLastCharacter(pendingLine));
+ return false;
+ }
+
+ @Override
+ boolean matchToBegin(Line line, StringBuilder pendingLine) {
+ boolean doContextBegin = matchEndOfString(pendingLine, commentStartTag)
+ && (commentNotStartTag == null || !matchEndOfString(pendingLine, commentNotStartTag));
+ if (doContextBegin) {
+ comment = new StringBuilder(commentStartTag);
+ }
+ return doContextBegin;
+ }
+
+ @Override
+ boolean matchWithEndOfLine(Line line, StringBuilder pendingLine) {
+ line.setComment(comment.toString());
+ comment = null;
+ return true;
+ }
+}
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/Source.java b/sonar-squid/src/main/java/org/sonar/squid/text/Source.java
new file mode 100644
index 00000000000..5087aaba331
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/Source.java
@@ -0,0 +1,128 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.sonar.squid.measures.Metric;
+import org.sonar.squid.recognizer.CodeRecognizer;
+
+public class Source {
+
+ private List<Line> lines = new ArrayList<Line>();
+ private CodeRecognizer codeRecognizer;
+ private Set<Integer> noSonarTagLines = new HashSet<Integer>();
+
+ public Source(Reader reader, CodeRecognizer codeRecognizer, String... additionalSingleLineCommentFlag) {
+ this.codeRecognizer = codeRecognizer;
+ LinesFactory linesFactory = new LinesFactory(reader, additionalSingleLineCommentFlag);
+ lines = linesFactory.getLines();
+ processLines();
+ }
+
+ public Source(String[] stringLines, CodeRecognizer codeRecognizer) {
+ this(new StringArrayReader(stringLines), codeRecognizer);
+ }
+
+ private void processLines() {
+ for (Line line : lines) {
+ computeBlankLine(line);
+ computeHeaderCommentLine(line);
+ computeCommentLine(line);
+ computeCommentBlankLine(line);
+ computeLineOfCode(line);
+ computeNoSonarTag(line);
+ line.deleteLineContent();
+ }
+ }
+
+ private void computeNoSonarTag(Line line) {
+ if (line.isThereNoSonarTag()) {
+ noSonarTagLines.add(line.getLineIndex());
+ }
+ }
+
+ private void computeLineOfCode(Line line) {
+ if (line.isThereCode()) {
+ line.setMeasure(Metric.LINES_OF_CODE, 1);
+ }
+ }
+
+ private void computeHeaderCommentLine(Line line) {
+ if (line.isThereComment() && !line.isThereBlankComment() && line.isThereLicenseHeaderComment()) {
+ line.setMeasure(Metric.HEADER_COMMENT_LINES, 1);
+ }
+ }
+
+ private void computeCommentLine(Line line) {
+ if (line.isThereComment() && !line.isThereBlankComment()) {
+ if (line.isThereJavadoc() || line.isThereLicenseHeaderComment()) {
+ line.setMeasure(Metric.COMMENT_LINES, 1);
+ return;
+ }
+
+ boolean isCommentedOutCode = codeRecognizer.isLineOfCode(line.getComment());
+ if (!isCommentedOutCode) {
+ line.setMeasure(Metric.COMMENT_LINES, 1);
+ } else {
+ line.setMeasure(Metric.COMMENTED_OUT_CODE_LINES, 1);
+ }
+ }
+ }
+
+ private void computeBlankLine(Line line) {
+ if (line.isBlank()) {
+ line.setMeasure(Metric.BLANK_LINES, 1);
+ }
+ }
+
+ private void computeCommentBlankLine(Line line) {
+ if (line.isThereBlankComment()) {
+ line.setMeasure(Metric.COMMENT_BLANK_LINES, 1);
+ }
+ }
+
+ public int getMeasure(Metric metric) {
+ return getMeasure(metric, 1, lines.size());
+ }
+
+ public int getMeasure(Metric metric, int fromLine, int toLine) {
+ if (toLine > lines.size()) {
+ throw new IllegalStateException("There are only " + lines.size() + " lines in the file and you're trying to reach line " + toLine);
+ }
+ if (fromLine < 1) {
+ throw new IllegalStateException("Line index starts from 1 and not from " + fromLine);
+ }
+
+ int measure = 0;
+ for (int index = fromLine; index < toLine + 1; index++) {
+ measure += lines.get(index - 1).getInt(metric);
+ }
+ return measure;
+ }
+
+ public Set<Integer> getNoSonarTagLines() {
+ return noSonarTagLines;
+ }
+}
diff --git a/sonar-squid/src/main/java/org/sonar/squid/text/StringArrayReader.java b/sonar-squid/src/main/java/org/sonar/squid/text/StringArrayReader.java
new file mode 100644
index 00000000000..1c923d0adc7
--- /dev/null
+++ b/sonar-squid/src/main/java/org/sonar/squid/text/StringArrayReader.java
@@ -0,0 +1,96 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.squid.text;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+public class StringArrayReader extends Reader {
+
+ private final StringReader stringReader;
+
+ enum EndOfLineDelimiter {
+ LF, CR_PLUS_LF, CR
+ }
+
+ public StringArrayReader(String[] lines) {
+ this(lines, EndOfLineDelimiter.LF);
+ }
+
+ public StringArrayReader(String[] lines, EndOfLineDelimiter endOfLineDelimiter) {
+ if (lines == null) {
+ throw new IllegalStateException("lines object can't be null.");
+ }
+ String content = convertArrayToStringAndAppendEndOfLine(lines, endOfLineDelimiter);
+ stringReader = new StringReader(content);
+ }
+
+ private String convertArrayToStringAndAppendEndOfLine(String[] lines, EndOfLineDelimiter endOfLineDelimiter) {
+ StringBuilder content = new StringBuilder();
+ for (int i = 0; i < lines.length; i++) {
+ content.append(lines[i]);
+ if (i != (lines.length - 1)) {
+ switch (endOfLineDelimiter) {
+ case LF:
+ content.append('\n');
+ break;
+ case CR:
+ content.append('\r');
+ break;
+ case CR_PLUS_LF:
+ content.append("\r\n");
+ break;
+ }
+ }
+ }
+ return content.toString();
+ }
+
+ @Override
+ public void close() throws IOException {
+ stringReader.close();
+ }
+
+ @Override
+ public boolean ready() throws IOException {
+ return stringReader.ready();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return stringReader.markSupported();
+ }
+
+ @Override
+ public void mark(int readAheadLimit) throws IOException {
+ stringReader.mark(readAheadLimit);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ stringReader.reset();
+ }
+
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ return stringReader.read(cbuf, off, len);
+ }
+}