From 7877c79af7a38c21655b1ed07e226fce80d78969 Mon Sep 17 00:00:00 2001 From: bellingard Date: Mon, 18 Oct 2010 15:25:51 +0000 Subject: [PATCH] [SONAR-1875] Improve CodeReaderFilter with channel capabilities http://jira.codehaus.org/browse/SONAR-1875 --- .../channel/ChannelCodeReaderFilter.java | 88 +++++++++++++++++++ .../java/org/sonar/channel/CodeBuffer.java | 6 +- .../org/sonar/channel/CodeReaderFilter.java | 44 +++++++++- .../org/sonar/channel/CodeBufferTest.java | 57 ++++++++++-- 4 files changed, 186 insertions(+), 9 deletions(-) create mode 100644 sonar-channel/src/main/java/org/sonar/channel/ChannelCodeReaderFilter.java diff --git a/sonar-channel/src/main/java/org/sonar/channel/ChannelCodeReaderFilter.java b/sonar-channel/src/main/java/org/sonar/channel/ChannelCodeReaderFilter.java new file mode 100644 index 00000000000..0834a52850e --- /dev/null +++ b/sonar-channel/src/main/java/org/sonar/channel/ChannelCodeReaderFilter.java @@ -0,0 +1,88 @@ +/* + * 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; + +/** + * This class is a special CodeReaderFilter that uses Channels to filter the character stream before it is passed to the main channels + * declared for the CodeReader. + * + */ +public final class ChannelCodeReaderFilter extends CodeReaderFilter { + + @SuppressWarnings("unchecked") + private Channel[] channels = new Channel[0]; + + private CodeReader internalCodeReader; + + private OUTPUT output; + + /** + * Creates a CodeReaderFilter that will use the provided Channels to filter the character stream it gets from its reader. And optionaly, + * it can push token to the provided output object. + * + * @param output + * the object that may accept tokens + * @param channels + * the different channels + */ + public ChannelCodeReaderFilter(OUTPUT output, Channel... channels) { + super(); + this.channels = channels; + this.output = output; + } + + /** + * ${@inheritDoc} + */ + @Override + public void setReader(Reader reader) { + super.setReader(reader); + internalCodeReader = new CodeReader(reader); + } + + /** + * ${@inheritDoc} + */ + @Override + public int read(char[] filteredBuffer, int offset, int lenght) throws IOException { + int initialOffset = offset; + while (offset < filteredBuffer.length) { + if (internalCodeReader.peek() == -1) { + break; + } + boolean consumed = false; + for (Channel channel : channels) { + if (channel.consume(internalCodeReader, output)) { + consumed = true; + break; + } + } + if ( !consumed) { + int charRead = internalCodeReader.pop(); + filteredBuffer[offset++] = (char) charRead; + } + } + return offset - initialOffset; + } + +} diff --git a/sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java b/sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java index b4288bad3ce..fcddb8d827f 100644 --- a/sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java +++ b/sonar-channel/src/main/java/org/sonar/channel/CodeBuffer.java @@ -253,6 +253,9 @@ public class CodeBuffer implements CharSequence { } } + /** + * Bridge class between CodeBuffer and CodeReaderFilter + */ final class Filter extends FilterReader { private CodeReaderFilter codeReaderFilter; @@ -260,6 +263,7 @@ public class CodeBuffer implements CharSequence { public Filter(Reader in, CodeReaderFilter codeReaderFilter) { super(in); this.codeReaderFilter = codeReaderFilter; + this.codeReaderFilter.setReader(in); } @Override @@ -269,7 +273,7 @@ public class CodeBuffer implements CharSequence { @Override public int read(char[] cbuf, int off, int len) throws IOException { - return codeReaderFilter.read(in, cbuf, off, len); + return codeReaderFilter.read(cbuf, off, len); } @Override diff --git a/sonar-channel/src/main/java/org/sonar/channel/CodeReaderFilter.java b/sonar-channel/src/main/java/org/sonar/channel/CodeReaderFilter.java index 4c63bcd9f90..da5a1dcb4f3 100644 --- a/sonar-channel/src/main/java/org/sonar/channel/CodeReaderFilter.java +++ b/sonar-channel/src/main/java/org/sonar/channel/CodeReaderFilter.java @@ -1,3 +1,22 @@ +/* + * 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; @@ -16,6 +35,27 @@ import java.io.Reader; */ public abstract class CodeReaderFilter { + private Reader reader; + + /** + * Returns the reader from which this class reads the character stream. + * + * @return the reader + */ + public Reader getReader() { + return reader; + } + + /** + * Sets the reader from which this class will read the character stream. + * + * @param reader + * the reader + */ + public void setReader(Reader reader) { + this.reader = reader; + } + /** * This method implements the filtering logic, that is: *
    @@ -27,8 +67,6 @@ public abstract class CodeReaderFilter { * and fill the given buffer to its full capacity with the filtered data. *
* - * @param reader - * the input character flow * @param filteredBuffer * the output buffer that must contain the filtered data * @param offset @@ -39,6 +77,6 @@ public abstract class CodeReaderFilter { * @throws IOException * If an I/O error occurs */ - public abstract int read(Reader reader, char[] filteredBuffer, int offset, int lenght) throws IOException; + public abstract int read(char[] filteredBuffer, int offset, int lenght) throws IOException; } diff --git a/sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java b/sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java index 05ac7704804..3807951eee0 100644 --- a/sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java +++ b/sonar-channel/src/test/java/org/sonar/channel/CodeBufferTest.java @@ -24,7 +24,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import java.io.IOException; -import java.io.Reader; import java.util.regex.Pattern; import org.junit.Test; @@ -175,14 +174,45 @@ public class CodeBufferTest { assertThat((char) code.pop(), is('-')); } + @Test + public void testChannelCodeReaderFilter() throws Exception { + // create a windowing channel that drops the 2 first characters, keeps 6 characters and drops the rest of the line + @SuppressWarnings({ "rawtypes", "unchecked" }) + CodeBuffer code = new CodeBuffer("0123456789\nABCDEFGHIJ", new ChannelCodeReaderFilter(null, new WindowingChannel())); + // test #charAt + assertEquals('2', code.charAt(0)); + assertEquals('7', code.charAt(5)); + assertEquals('\n', code.charAt(6)); + assertEquals('C', code.charAt(7)); + assertEquals('H', code.charAt(12)); + assertEquals( -1, code.intAt(13)); + // test peek and pop + assertThat((char) code.peek(), is('2')); + assertThat((char) code.pop(), is('2')); + assertThat((char) code.pop(), is('3')); + assertThat((char) code.pop(), is('4')); + assertThat((char) code.pop(), is('5')); + assertThat((char) code.pop(), is('6')); + assertThat((char) code.pop(), is('7'));// and 8 shouldn't show up + assertThat((char) code.pop(), is('\n')); + assertThat((char) code.peek(), is('C')); + assertThat((char) code.pop(), is('C')); + assertThat((char) code.pop(), is('D')); + assertThat((char) code.pop(), is('E')); + assertThat((char) code.pop(), is('F')); + assertThat((char) code.pop(), is('G')); + assertThat((char) code.pop(), is('H')); + assertThat(code.pop(), is( -1)); + } + class ReplaceNumbersFilter extends CodeReaderFilter { private Pattern pattern = Pattern.compile("\\d"); private String REPLACEMENT = "-"; - public int read(Reader in, char[] cbuf, int off, int len) throws IOException { + public int read(char[] cbuf, int off, int len) throws IOException { char[] tempBuffer = new char[cbuf.length]; - int charCount = in.read(tempBuffer, off, len); + int charCount = getReader().read(tempBuffer, off, len); if (charCount != -1) { String filteredString = pattern.matcher(new String(tempBuffer)).replaceAll(REPLACEMENT); System.arraycopy(filteredString.toCharArray(), 0, cbuf, 0, tempBuffer.length); @@ -196,9 +226,9 @@ public class CodeBufferTest { private Pattern pattern = Pattern.compile("[a-zA-Z]"); private String REPLACEMENT = "*"; - public int read(Reader in, char[] cbuf, int off, int len) throws IOException { + public int read(char[] cbuf, int off, int len) throws IOException { char[] tempBuffer = new char[cbuf.length]; - int charCount = in.read(tempBuffer, off, len); + int charCount = getReader().read(tempBuffer, off, len); if (charCount != -1) { String filteredString = pattern.matcher(new String(tempBuffer)).replaceAll(REPLACEMENT); System.arraycopy(filteredString.toCharArray(), 0, cbuf, 0, tempBuffer.length); @@ -207,4 +237,21 @@ public class CodeBufferTest { } } + @SuppressWarnings("rawtypes") + class WindowingChannel extends Channel { + + @Override + public boolean consume(CodeReader code, Object output) { + int columnPosition = code.getColumnPosition(); + if (code.peek() == '\n') { + return false; + } + if (columnPosition < 2 || columnPosition > 7) { + code.pop(); + return true; + } + return false; + } + } + } -- 2.39.5