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 private static final int HISTORY_SIZE = 3; 035 private static final int PEEK_TO_WINDOW_SIZE = 20; 036 037 private final Reader reader; 038 private int[] lastChars = new int[HISTORY_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 this.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 this.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 = this.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 }