diff options
Diffstat (limited to 'sonar-channel/src/main/java')
6 files changed, 580 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); +} |