http://jira.codehaus.org/browse/SONAR-1875tags/2.6
@@ -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<OUTPUT> extends CodeReaderFilter { | |||
@SuppressWarnings("unchecked") | |||
private Channel<OUTPUT>[] 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<OUTPUT>... 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<OUTPUT> channel : channels) { | |||
if (channel.consume(internalCodeReader, output)) { | |||
consumed = true; | |||
break; | |||
} | |||
} | |||
if ( !consumed) { | |||
int charRead = internalCodeReader.pop(); | |||
filteredBuffer[offset++] = (char) charRead; | |||
} | |||
} | |||
return offset - initialOffset; | |||
} | |||
} |
@@ -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 |
@@ -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: | |||
* <ul> | |||
@@ -27,8 +67,6 @@ public abstract class CodeReaderFilter { | |||
* and fill the given buffer to its full capacity with the filtered data.</li> | |||
* </ul> | |||
* | |||
* @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; | |||
} |
@@ -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; | |||
} | |||
} | |||
} |