summaryrefslogtreecommitdiffstats
path: root/sonar-channel/src
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-channel/src
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-channel/src')
-rw-r--r--sonar-channel/src/main/java/org/sonar/channel/Channel.java25
-rw-r--r--sonar-channel/src/main/java/org/sonar/channel/ChannelDispatcher.java68
-rw-r--r--sonar-channel/src/main/java/org/sonar/channel/ChannelException.java31
-rw-r--r--sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java251
-rw-r--r--sonar-channel/src/main/java/org/sonar/channel/CodeReader.java180
-rw-r--r--sonar-channel/src/main/java/org/sonar/channel/EndMatcher.java25
-rw-r--r--sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java119
-rw-r--r--sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java125
8 files changed, 824 insertions, 0 deletions
diff --git a/sonar-channel/src/main/java/org/sonar/channel/Channel.java b/sonar-channel/src/main/java/org/sonar/channel/Channel.java
new file mode 100644
index 00000000000..198ad995f61
--- /dev/null
+++ b/sonar-channel/src/main/java/org/sonar/channel/Channel.java
@@ -0,0 +1,25 @@
+/*
+ * 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.channel;
+
+public abstract class Channel<OUTPUT> {
+
+ public abstract boolean consume(CodeReader code, OUTPUT output);
+}
diff --git a/sonar-channel/src/main/java/org/sonar/channel/ChannelDispatcher.java b/sonar-channel/src/main/java/org/sonar/channel/ChannelDispatcher.java
new file mode 100644
index 00000000000..f307c2c3335
--- /dev/null
+++ b/sonar-channel/src/main/java/org/sonar/channel/ChannelDispatcher.java
@@ -0,0 +1,68 @@
+/*
+ * 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.channel;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChannelDispatcher<OUTPUT> extends Channel<OUTPUT> {
+
+ private static final Logger logger = LoggerFactory.getLogger(ChannelDispatcher.class);
+ private final boolean failIfNoChannelToConsumeOneCharacter;
+
+ private final Channel[] channels;
+
+ public ChannelDispatcher(List<Channel> tokenizers) {
+ this(tokenizers, false);
+ }
+
+ public ChannelDispatcher(List<Channel> tokenizers, boolean failIfNoChannelToConsumeOneCharacter) {
+ this.channels = tokenizers.toArray(new Channel[0]); // NOSONAR, lack of performance is not an issue here
+ this.failIfNoChannelToConsumeOneCharacter = failIfNoChannelToConsumeOneCharacter;
+ }
+
+ public boolean consume(CodeReader code, OUTPUT output) {
+ int nextChar = code.peek();
+ while (nextChar != -1) {
+ boolean channelConsumed = false;
+ for (Channel<OUTPUT> channel : channels) {
+ if (channel.consume(code, output)) {
+ channelConsumed = true;
+ break;
+ }
+ }
+ if ( !channelConsumed) {
+ String message = "None of the channel has been able to handle character '" + (char) code.peek() + "' (decimal value " + code.peek()
+ + ") at line "
+ + code.getLinePosition() + ", column " + code.getColumnPosition();
+ if (failIfNoChannelToConsumeOneCharacter) {
+ throw new IllegalStateException(message);
+ }
+ logger.debug(message);
+ code.pop();
+ }
+ nextChar = code.peek();
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/sonar-channel/src/main/java/org/sonar/channel/ChannelException.java b/sonar-channel/src/main/java/org/sonar/channel/ChannelException.java
new file mode 100644
index 00000000000..1b1f4c9f3d0
--- /dev/null
+++ b/sonar-channel/src/main/java/org/sonar/channel/ChannelException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.channel;
+
+public class ChannelException extends RuntimeException {
+
+ public ChannelException(String message, Exception e) {
+ super(message, e);
+ }
+
+ public ChannelException(String message) {
+ super(message);
+ }
+}
diff --git a/sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java b/sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java
new file mode 100644
index 00000000000..f726f0a9645
--- /dev/null
+++ b/sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java
@@ -0,0 +1,251 @@
+/*
+ * 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.channel;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.apache.commons.io.IOUtils;
+
+/**
+ * The CodeBuffer class provides all the basic features required to manipulate a source code character stream.
+ * Those features are :
+ * <ul>
+ * <li>Read and consume next source code character : pop()</li>
+ * <li>Retrieve last consumed character : lastChar()</li>
+ * <li>Read without consuming next source code character : peek()</li>
+ * <li>Read without consuming character at the specified index after the cursor</li>
+ * <li>Position of the pending cursor : line and column</li>
+ * </ul>
+ */
+public class CodeBuffer implements CharSequence {
+
+ private final Reader code;
+ private int lastChar = -1;
+ private Cursor cursor;
+ private final static int DEFAULT_BUFFER_CAPACITY = 8000;
+ private int bufferCapacity;
+ private char[] buffer;
+ private int bufferPosition = 0;
+ private int bufferSize = 0;
+ private static final char LF = '\n';
+ private static final char CR = '\r';
+ private boolean recordingMode = false;
+ private StringBuilder recordedCharacters = new StringBuilder();
+
+ public CodeBuffer(Reader code) {
+ this(code, DEFAULT_BUFFER_CAPACITY);
+ }
+
+ private CodeBuffer(Reader code, int bufferCapacity) {
+ this.code = code;
+ lastChar = -1;
+ cursor = new Cursor();
+ this.bufferCapacity = bufferCapacity;
+ buffer = new char[bufferCapacity];
+ fillBuffer();
+ }
+
+ public CodeBuffer(String code) {
+ this(new StringReader(code));
+ }
+
+ protected CodeBuffer(String code, int bufferCapacity) {
+ this(new StringReader(code), bufferCapacity);
+ }
+
+ /**
+ * Read and consume the next character
+ *
+ * @return the next character or -1 if the end of the stream is reached
+ */
+ public final int pop() {
+ if (bufferPosition == bufferSize) {
+ fillBuffer();
+ }
+ if (bufferSize == 0) {
+ return -1;
+ }
+ int character = buffer[bufferPosition++];
+ if (character == LF || character == CR) {
+ if ((lastChar != LF && lastChar != CR) || lastChar == character || lastChar == LF) {
+ cursor.line++;
+ }
+ cursor.column = 0;
+ } else {
+ cursor.column++;
+ }
+ if (recordingMode) {
+ recordedCharacters.append((char) character);
+ }
+ lastChar = character;
+ return character;
+ }
+
+ private int fillBuffer() {
+ try {
+ int offset = bufferSize - bufferPosition;
+ if (offset != 0) {
+ System.arraycopy(buffer, bufferPosition, buffer, 0, bufferSize - bufferPosition);
+ }
+ bufferPosition = 0;
+ int numberOfChars = code.read(buffer, offset, bufferCapacity - offset);
+ if (numberOfChars == -1) {
+ numberOfChars = 0;
+ }
+ bufferSize = numberOfChars + offset;
+ return offset;
+ } catch (IOException e) {
+ throw new ChannelException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Get the last consumed character
+ *
+ * @return the last character or -1 if the no character has been yet consumed
+ */
+ public final int lastChar() {
+ return lastChar;
+ }
+
+ /**
+ * Read without consuming the next character
+ *
+ * @return the next character or -1 if the end of the stream is reached
+ */
+ public final int peek() {
+ return intAt(0);
+ }
+
+ /**
+ * Close the stream
+ */
+ public final void close() {
+ IOUtils.closeQuietly(code);
+ }
+
+ /**
+ * @return the current line of the cursor
+ */
+ public final int getLinePosition() {
+ return cursor.line;
+ }
+
+ public final Cursor getCursor() {
+ return cursor;
+ }
+
+ /**
+ * @return the current column of the cursor
+ */
+ public final int getColumnPosition() {
+ return cursor.column;
+ }
+
+ /**
+ * Overrides the current column position
+ */
+ public final CodeBuffer setColumnPosition(int cp) {
+ this.cursor.column = cp;
+ return this;
+ }
+
+ /**
+ * Overrides the current line position
+ */
+ public final void setLinePosition(int lp) {
+ this.cursor.line = lp;
+ }
+
+ public final void startRecording() {
+ recordingMode = true;
+ }
+
+ public final CharSequence stopRecording() {
+ recordingMode = false;
+ CharSequence result = recordedCharacters;
+ recordedCharacters = new StringBuilder();
+ return result;
+ }
+
+ /**
+ * Returns the character at the specified index after the cursor without consuming it
+ *
+ * @param index
+ * the index of the character to be returned
+ * @return the desired character
+ * @see java.lang.CharSequence#charAt(int)
+ */
+ public final char charAt(int index) {
+ return (char) intAt(index);
+ }
+
+ protected final int intAt(int index) {
+ if (bufferPosition + index > bufferSize - 1) {
+ fillBuffer();
+ }
+ if (bufferPosition + index > bufferSize - 1) {
+ return -1;
+ }
+ return buffer[bufferPosition + index];
+ }
+
+ public final int length() {
+ return bufferSize;
+ }
+
+ public final CharSequence subSequence(int start, int end) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append("CodeReader(");
+ result.append("line:" + cursor.line);
+ result.append("|column:" + cursor.column);
+ result.append("|cursor value:'" + (char) peek() + "'");
+ result.append(")");
+ return result.toString();
+ }
+
+ public final class Cursor {
+
+ private int line = 1;
+ private int column = 0;
+
+ public int getLine() {
+ return line;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ public Cursor clone() {
+ Cursor clone = new Cursor();
+ clone.column = column;
+ clone.line = line;
+ return clone;
+ }
+ }
+}
diff --git a/sonar-channel/src/main/java/org/sonar/channel/CodeReader.java b/sonar-channel/src/main/java/org/sonar/channel/CodeReader.java
new file mode 100644
index 00000000000..f894ca7fd15
--- /dev/null
+++ b/sonar-channel/src/main/java/org/sonar/channel/CodeReader.java
@@ -0,0 +1,180 @@
+/*
+ * 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.channel;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.regex.Matcher;
+
+/**
+ * The CodeReader class provides all the basic features to lex a source code.
+ * Those features are :
+ * <ul>
+ * <li>Read and consume next characters until a regular expression is matched</li>
+ * </ul>
+ */
+public class CodeReader extends CodeBuffer {
+
+ private Cursor previousCursor;
+
+ public CodeReader(Reader code) {
+ super(code);
+ }
+
+ public CodeReader(String code) {
+ super(code);
+ }
+
+ protected CodeReader(String code, int bufferCapacity) {
+ super(code, bufferCapacity);
+ }
+
+ /**
+ * Read and consume the next character
+ *
+ * @param appendable
+ * the read character is appended to appendable
+ */
+ public final void pop(Appendable appendable) {
+ try {
+ appendable.append((char) pop());
+ } catch (IOException e) {
+ throw new ChannelException(e.getMessage());
+ }
+ }
+
+ /**
+ * Read without consuming the next characters
+ *
+ * @param length
+ * number of character to read
+ * @return array of characters
+ */
+ public final char[] peek(int length) {
+ char[] result = new char[length];
+ int index = 0;
+ int nextChar = intAt(index);
+ while (nextChar != -1 && index < length) {
+ result[index] = (char) nextChar;
+ nextChar = intAt(++index);
+ }
+ return result;
+ }
+
+ /**
+ * Read without consuming the next characters until a condition is reached (EndMatcher)
+ *
+ * @param matcher
+ * the EndMatcher used to stop the reading
+ * @param appendable
+ * the read characters is appended to appendable
+ */
+ public final void peekTo(EndMatcher matcher, Appendable appendable) {
+ int index = 0;
+ char nextChar = charAt(index);
+ try {
+ while ( !matcher.match(nextChar) && nextChar != -1) {
+ appendable.append(nextChar);
+ nextChar = charAt(++index);
+ }
+ } catch (IOException e) {
+ throw new ChannelException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * @deprecated see peekTo(EndMatcher matcher, Appendable appendable)
+ */
+ @Deprecated
+ public final String peekTo(EndMatcher matcher) {
+ StringBuilder sb = new StringBuilder();
+ peekTo(matcher, sb);
+ return sb.toString();
+ }
+
+ /**
+ * @deprecated see popTo(Matcher matcher, Appendable appendable)
+ */
+ @Deprecated
+ public final void popTo(EndMatcher matcher, Appendable appendable) {
+ previousCursor = getCursor().clone();
+ try {
+ do {
+ appendable.append((char) pop());
+ } while ( !matcher.match(peek()) && peek() != -1);
+ } catch (IOException e) {
+ throw new ChannelException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Read and consume the next characters according to a given regular expression
+ *
+ * @param matcher
+ * the regular expression matcher
+ * @param appendable
+ * the consumed characters are appended to this appendable
+ * @return number of consumed characters or -1 if the next input sequence doesn't match this matcher's pattern
+ */
+ public final int popTo(Matcher matcher, Appendable appendable) {
+ return popTo(matcher, null, appendable);
+ }
+
+ /**
+ * Read and consume the next characters according to a given regular expression. Moreover the character sequence immediately following the
+ * desired characters must also match a given regular expression.
+ *
+ * @param matcher
+ * the Matcher used to try consuming next characters
+ * @param afterMatcher
+ * the Matcher used to check character sequence immediately following the consumed characters
+ * @param appendable
+ * the consumed characters are appended to this appendable
+ * @return number of consumed characters or -1 if one of the two Matchers doesn't match
+ */
+ public final int popTo(Matcher matcher, Matcher afterMatcher, Appendable appendable) {
+ try {
+ matcher.reset(this);
+ if (matcher.lookingAt()) {
+ if (afterMatcher != null) {
+ afterMatcher.reset(this);
+ afterMatcher.region(matcher.end(), length());
+ if ( !afterMatcher.lookingAt()) {
+ return -1;
+ }
+ }
+ previousCursor = getCursor().clone();
+ for (int i = 0; i < matcher.end(); i++) {
+ appendable.append((char) pop());
+ }
+ return matcher.end();
+ }
+ } catch (IndexOutOfBoundsException e) {
+ return -1;
+ } catch (IOException e) {
+ throw new ChannelException(e.getMessage(), e);
+ }
+ return -1;
+ }
+
+ public final Cursor getPreviousCursor() {
+ return previousCursor;
+ }
+}
diff --git a/sonar-channel/src/main/java/org/sonar/channel/EndMatcher.java b/sonar-channel/src/main/java/org/sonar/channel/EndMatcher.java
new file mode 100644
index 00000000000..e74d2b76643
--- /dev/null
+++ b/sonar-channel/src/main/java/org/sonar/channel/EndMatcher.java
@@ -0,0 +1,25 @@
+/*
+ * 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.channel;
+
+public interface EndMatcher {
+
+ boolean match(int toMatch);
+}
diff --git a/sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java b/sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java
new file mode 100644
index 00000000000..eedf28ae0ad
--- /dev/null
+++ b/sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.channel;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class CodeBufferTest {
+
+ @Test
+ public void testPop() {
+ CodeBuffer code = new CodeBuffer("pa");
+ assertThat((char) code.pop(), is('p'));
+ assertThat((char) code.pop(), is('a'));
+ assertThat(code.pop(), is( -1));
+ }
+
+ @Test
+ public void testPeek() {
+ CodeBuffer code = new CodeBuffer("pa");
+ assertThat((char) code.peek(), is('p'));
+ assertThat((char) code.peek(), is('p'));
+ code.pop();
+ assertThat((char) code.peek(), is('a'));
+ code.pop();
+ assertThat(code.peek(), is( -1));
+ }
+
+ @Test
+ public void testLastCharacter() {
+ CodeReader reader = new CodeReader("bar");
+ assertThat(reader.lastChar(), is( -1));
+ reader.pop();
+ assertThat((char) reader.lastChar(), is('b'));
+ }
+
+ @Test
+ public void testGetColumnAndLinePosition() {
+ CodeReader reader = new CodeReader("pa\nc\r\ns\r\n\r\n");
+ assertThat(reader.getColumnPosition(), is(0));
+ assertThat(reader.getLinePosition(), is(1));
+ reader.pop(); // p
+ reader.pop(); // a
+ assertThat(reader.getColumnPosition(), is(2));
+ assertThat(reader.getLinePosition(), is(1));
+ reader.peek(); // \n
+ reader.lastChar(); // a
+ assertThat(reader.getColumnPosition(), is(2));
+ assertThat(reader.getLinePosition(), is(1));
+ reader.pop(); // \n
+ assertThat(reader.getColumnPosition(), is(0));
+ assertThat(reader.getLinePosition(), is(2));
+ reader.pop(); // c
+ assertThat(reader.getColumnPosition(), is(1));
+ assertThat(reader.getLinePosition(), is(2));
+ reader.pop(); // \r
+ reader.pop(); // \n
+ assertThat(reader.getColumnPosition(), is(0));
+ assertThat(reader.getLinePosition(), is(3));
+ assertThat((char) reader.pop(), is('s'));
+ reader.pop(); // \r
+ assertThat(reader.getColumnPosition(), is(0));
+ assertThat(reader.getLinePosition(), is(4));
+ reader.pop(); // \n
+ assertThat(reader.getColumnPosition(), is(0));
+ assertThat(reader.getLinePosition(), is(4));
+ reader.pop(); // \r
+ reader.pop(); // \n
+ assertThat(reader.getColumnPosition(), is(0));
+ assertThat(reader.getLinePosition(), is(5));
+ }
+
+ @Test
+ public void testStartAndStopRecording() {
+ CodeReader reader = new CodeReader("123456");
+ reader.pop();
+ assertEquals("", reader.stopRecording().toString());
+
+ reader.startRecording();
+ reader.pop();
+ reader.pop();
+ reader.peek();
+ assertEquals("23", reader.stopRecording().toString());
+ assertEquals("", reader.stopRecording().toString());
+ }
+
+ @Test
+ public void testCharAt() {
+ CodeReader reader = new CodeReader("123456");
+ assertEquals('1', reader.charAt(0));
+ assertEquals('6', reader.charAt(5));
+ }
+
+ @Test
+ public void testCharAtIndexOutOfBoundsException() {
+ CodeReader reader = new CodeReader("12345");
+ assertEquals(reader.charAt(5), (char) -1);
+ }
+}
diff --git a/sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java b/sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java
new file mode 100644
index 00000000000..7c195bff527
--- /dev/null
+++ b/sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.channel;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.number.OrderingComparisons.lessThan;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.io.StringReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+public class CodeReaderTest {
+
+ @Test
+ public void testPopWithAppendable() {
+ CodeReader reader = new CodeReader("package org.sonar;");
+
+ StringBuilder sw = new StringBuilder();
+ reader.pop(sw);
+ assertEquals("p", sw.toString());
+ reader.pop(sw);
+ assertEquals("pa", sw.toString());
+
+ }
+
+ @Test
+ public void testPeekACharArray() {
+ CodeReader reader = new CodeReader(new StringReader("bar"));
+ char[] chars = reader.peek(2);
+ assertThat(chars.length, is(2));
+ assertThat(chars[0], is('b'));
+ assertThat(chars[1], is('a'));
+ }
+
+ @Test
+ public void testPeekTo() {
+ CodeReader reader = new CodeReader(new StringReader("package org.sonar;"));
+ StringBuilder result = new StringBuilder();
+ reader.peekTo(new EndMatcher() {
+
+ public boolean match(int endFlag) {
+ return 'r' == (char) endFlag;
+ }
+ }, result);
+ assertEquals("package o", result.toString());
+ assertThat(reader.peek(), is((int) 'p')); // never called pop()
+ }
+
+ @Test
+ public void testPopTo() {
+ CodeReader reader = new CodeReader(new StringReader("package org.sonar;"));
+ StringBuilder result = new StringBuilder();
+ reader.popTo(new EndMatcher() {
+
+ public boolean match(int endFlag) {
+ return 'r' == (char) endFlag;
+ }
+ }, result);
+ assertThat(result.toString(), is("package o"));
+ assertThat(reader.peek(), is((int) 'r'));
+ CodeReader.Cursor previousCursor = reader.getPreviousCursor();
+ assertThat(previousCursor.getColumn(), is(0));
+ assertThat(previousCursor.getLine(), is(1));
+ }
+
+ @Test
+ public void testPeekToAndReachingTheBufferLimit() {
+ CodeReader reader = new CodeReader("word1 word2 word2", 10);
+ for (int i = 0; i < 6; i++) {
+ reader.pop();
+ }
+ StringBuilder result = new StringBuilder();
+ reader.peekTo(new EndMatcher() {
+
+ public boolean match(int endFlag) {
+ return ' ' == (char) endFlag;
+ }
+ }, result);
+ assertEquals("word2", result.toString());
+ }
+
+ @Test
+ public void testPopToWithRegex() {
+ CodeReader reader = new CodeReader(new StringReader("123ABC"));
+ StringBuilder token = new StringBuilder();
+ assertEquals(3, reader.popTo(Pattern.compile("\\d+").matcher(new String()), token));
+ assertEquals("123", token.toString());
+ assertEquals( -1, reader.popTo(Pattern.compile("\\d+").matcher(new String()), token));
+ assertEquals(3, reader.popTo(Pattern.compile("\\w+").matcher(new String()), token));
+ assertEquals("123ABC", token.toString());
+ assertEquals( -1, reader.popTo(Pattern.compile("\\w+").matcher(new String()), token));
+ }
+
+ @Test
+ public void testPopToWithRegexAndFollowingMatcher() {
+ Matcher digitMatcher = Pattern.compile("\\d+").matcher(new String());
+ Matcher alphabeticMatcher = Pattern.compile("[a-zA-Z]").matcher(new String());
+ StringBuilder token = new StringBuilder();
+ assertEquals( -1, new CodeReader(new StringReader("123 ABC")).popTo(digitMatcher, alphabeticMatcher, token));
+ assertEquals("", token.toString());
+ assertEquals(3, new CodeReader(new StringReader("123ABC")).popTo(digitMatcher, alphabeticMatcher, token));
+ assertEquals("123", token.toString());
+ }
+}