aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-channel/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-channel/src/main/java')
-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
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);
+}