001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.channel;
021
022 import java.io.IOException;
023 import java.io.Reader;
024 import java.util.regex.Matcher;
025
026 /**
027 * The CodeReader class provides some advanced features to read a source code. The most important one is the ability to try consuming the
028 * next characters in the stream according to a regular expression.
029 */
030 public class CodeReader extends CodeBuffer {
031
032 private Cursor previousCursor;
033
034 /*
035 * Constructor needed to be backward compatible (before using CodeReaderFilter)
036 */
037 public CodeReader(Reader code) {
038 super(code, new CodeReaderConfiguration());
039 }
040
041 /*
042 * Constructor needed to be backward compatible (before using CodeReaderFilter)
043 */
044 public CodeReader(String code) {
045 super(code, new CodeReaderConfiguration());
046 }
047
048 /**
049 * Creates a code reader with specific configuration parameters.
050 * Note that this constructor will read everything from reader and will close it.
051 *
052 * @param code
053 * the Reader to read code from
054 * @param configuration
055 * the configuration parameters
056 */
057 public CodeReader(Reader code, CodeReaderConfiguration configuration) {
058 super(code, configuration);
059 }
060
061 /**
062 * Creates a code reader with specific configuration parameters.
063 *
064 * @param code
065 * the code itself
066 * @param configuration
067 * the configuration parameters
068 */
069 public CodeReader(String code, CodeReaderConfiguration configuration) {
070 super(code, configuration);
071 }
072
073 /**
074 * Read and consume the next character
075 *
076 * @param appendable
077 * the read character is appended to appendable
078 */
079 public final void pop(Appendable appendable) {
080 try {
081 appendable.append((char) pop());
082 } catch (IOException e) {
083 throw new ChannelException(e.getMessage(), e);
084 }
085 }
086
087 /**
088 * Read without consuming the next characters
089 *
090 * @param length
091 * number of character to read
092 * @return array of characters
093 */
094 public final char[] peek(int length) {
095 char[] result = new char[length];
096 int index = 0;
097 int nextChar = intAt(index);
098 while (nextChar != -1 && index < length) {
099 result[index] = (char) nextChar;
100 nextChar = intAt(++index);
101 }
102 return result;
103 }
104
105 /**
106 * Read without consuming the next characters until a condition is reached (EndMatcher)
107 *
108 * @param matcher
109 * the EndMatcher used to stop the reading
110 * @param appendable
111 * the read characters is appended to appendable
112 */
113 public final void peekTo(EndMatcher matcher, Appendable appendable) {
114 int index = 0;
115 char nextChar = charAt(index);
116 try {
117 while ( !matcher.match(nextChar) && nextChar != -1) {
118 appendable.append(nextChar);
119 nextChar = charAt(++index);
120 }
121 } catch (IOException e) {
122 throw new ChannelException(e.getMessage(), e);
123 }
124 }
125
126 /**
127 * @deprecated in 2.2, use {@link #peekTo(EndMatcher matcher, Appendable appendable)} instead
128 */
129 @Deprecated
130 public final String peekTo(EndMatcher matcher) {
131 StringBuilder sb = new StringBuilder();
132 peekTo(matcher, sb);
133 return sb.toString();
134 }
135
136 /**
137 * @deprecated in 2.2, use {@link #popTo(Matcher matcher, Appendable appendable)} instead
138 */
139 @Deprecated
140 public final void popTo(EndMatcher matcher, Appendable appendable) {
141 previousCursor = getCursor().clone();
142 try {
143 do {
144 appendable.append((char) pop());
145 } while ( !matcher.match(peek()) && peek() != -1);
146 } catch (IOException e) {
147 throw new ChannelException(e.getMessage(), e);
148 }
149 }
150
151 /**
152 * Read and consume the next characters according to a given regular expression
153 *
154 * @param matcher
155 * the regular expression matcher
156 * @param appendable
157 * the consumed characters are appended to this appendable
158 * @return number of consumed characters or -1 if the next input sequence doesn't match this matcher's pattern
159 */
160 public final int popTo(Matcher matcher, Appendable appendable) {
161 return popTo(matcher, null, appendable);
162 }
163
164 /**
165 * Read and consume the next characters according to a given regular expression. Moreover the character sequence immediately following the
166 * desired characters must also match a given regular expression.
167 *
168 * @param matcher
169 * the Matcher used to try consuming next characters
170 * @param afterMatcher
171 * the Matcher used to check character sequence immediately following the consumed characters
172 * @param appendable
173 * the consumed characters are appended to this appendable
174 * @return number of consumed characters or -1 if one of the two Matchers doesn't match
175 */
176 public final int popTo(Matcher matcher, Matcher afterMatcher, Appendable appendable) {
177 try {
178 matcher.reset(this);
179 if (matcher.lookingAt()) {
180 if (afterMatcher != null) {
181 afterMatcher.reset(this);
182 afterMatcher.region(matcher.end(), length());
183 if ( !afterMatcher.lookingAt()) {
184 return -1;
185 }
186 }
187 previousCursor = getCursor().clone();
188 for (int i = 0; i < matcher.end(); i++) {
189 appendable.append((char) pop());
190 }
191 return matcher.end();
192 }
193 } catch (IndexOutOfBoundsException e) {
194 return -1;
195 } catch (IOException e) {
196 throw new ChannelException(e.getMessage(), e);
197 }
198 return -1;
199 }
200
201 public final Cursor getPreviousCursor() {
202 return previousCursor;
203 }
204 }