001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 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 }