001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
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.colorizer;
021
022 import java.io.BufferedReader;
023 import java.io.CharArrayWriter;
024 import java.io.IOException;
025 import java.io.Reader;
026 import java.io.StringReader;
027 import java.util.HashMap;
028 import java.util.Map;
029
030 import org.apache.commons.io.IOUtils;
031
032 public class CodeReader {
033
034 public static final int LAST_CHARS_MAX_SIZE = 3;
035 public static final int PEEK_TO_WINDOW_SIZE = 20;
036
037 private final Reader reader;
038 private int[] lastChars = new int[LAST_CHARS_MAX_SIZE];
039 private Map variables = new HashMap(); // stateful data
040
041 public CodeReader(Reader reader) {
042 this.reader = new BufferedReader(reader);
043 for (int i = 0; i < lastChars.length; i++) {
044 lastChars[i] = -1;
045 }
046 }
047
048 public CodeReader(String code) {
049 this(new StringReader(code));
050 }
051
052 private int read() throws IOException {
053 if (!reader.ready()) {
054 return -1;
055 }
056 return reader.read();
057 }
058
059 public final void mark(int readAheadLimit) throws IOException {
060 if (reader.markSupported()) {
061 throw new ColorizerException("Mark are not supported on provided Reader.");
062 }
063 reader.mark(readAheadLimit);
064 }
065
066 public final int lastChar() {
067 return lastChars[0];
068 }
069
070 public final int peek() {
071 try {
072 reader.mark(1);
073 int nextChar = read();
074 reader.reset();
075 return nextChar;
076 } catch (IOException e) {
077 throw new ColorizerException("Unable to read on input stream.", e);
078 }
079 }
080
081 public final int pop() {
082 try {
083 setLastChar(read());
084 return lastChar();
085 } catch (IOException e) {
086 throw new ColorizerException("Unable to read on input stream.", e);
087 }
088 }
089
090 private void setLastChar(int read) {
091 for (int i = lastChars.length - 1; i > 0; i--) {
092 lastChars[i] = lastChars[i - 1];
093 }
094 lastChars[0] = read;
095
096 }
097
098 public String popTo(EndTokenMatcher matcher) {
099 CharArrayWriter result = new CharArrayWriter();
100 result.append((char) pop());
101 while (!matcher.match(peek()) && peek() != -1) {
102 result.append((char) pop());
103 }
104 return result.toString();
105 }
106
107 public String peekTo(EndTokenMatcher matcher) {
108 int lookAheadLimit = PEEK_TO_WINDOW_SIZE;
109 int lookAheadLimitMultiplier = 1;
110 String result;
111 do {
112 lookAheadLimit = lookAheadLimit * lookAheadLimitMultiplier++;
113 result = peekTo(matcher, lookAheadLimit);
114 } while (result.length() == lookAheadLimit);
115 return result;
116 }
117
118 protected String peekTo(EndTokenMatcher matcher, int lookAheadLimit) {
119 try {
120 CharArrayWriter result = new CharArrayWriter();
121 reader.mark(lookAheadLimit);
122 int index = 0;
123 int nextChar;
124 do {
125 nextChar = reader.read();
126 index++;
127 if (!matcher.match(nextChar)) {
128 result.append((char) nextChar);
129 }
130 } while (!matcher.match(nextChar) && index != lookAheadLimit);
131 reader.reset();
132 return result.toString();
133 } catch (IOException e) {
134 throw new ColorizerException("Unable to read on input stream.", e);
135 }
136 }
137
138 public void close() {
139 IOUtils.closeQuietly(reader);
140 }
141
142 public char[] peek(int index) {
143 try {
144 char[] result = new char[index];
145 reader.mark(index);
146 reader.read(result, 0, index);
147 reader.reset();
148 return result;
149 } catch (IOException e) {
150 throw new ColorizerException("Unable to read on input stream.", e);
151 }
152 }
153
154 public int lastChar(int index) {
155 return lastChars[index];
156 }
157
158 /**
159 * Save a stateful variable.
160 * @param key can NOT be null
161 * @param value can be null
162 */
163 public void setVariable(Object key, Object value) {
164 variables.put(key, value);
165 }
166
167 /**
168 * Get a stateful variable. Return null if not found.
169 */
170 public Object getVariable(Object key) {
171 return variables.get(key);
172 }
173
174 /**
175 * Get a stateful variable. Return the default value if not found.
176 */
177 public Object getVariable(Object key, Object defaultValue) {
178 Object result = variables.get(key);
179 if (result==null) {
180 result = defaultValue;
181 }
182 return result;
183 }
184
185 /**
186 * All stateful variables
187 */
188 public Map getVariables() {
189 return variables;
190 }
191 }