001 /* Matcher.java -- Instance of a regular expression applied to a char sequence. 002 Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package java.util.regex; 040 041 import gnu.java.util.regex.CharIndexed; 042 import gnu.java.util.regex.RE; 043 import gnu.java.util.regex.REMatch; 044 045 /** 046 * Instance of a regular expression applied to a char sequence. 047 * 048 * @since 1.4 049 */ 050 public final class Matcher implements MatchResult 051 { 052 private Pattern pattern; 053 private CharSequence input; 054 // We use CharIndexed as an input object to the getMatch method in order 055 // that /\G/ (the end of the previous match) may work. The information 056 // of the previous match is stored in the CharIndexed object. 057 private CharIndexed inputCharIndexed; 058 private int position; 059 private int appendPosition; 060 private REMatch match; 061 062 Matcher(Pattern pattern, CharSequence input) 063 { 064 this.pattern = pattern; 065 this.input = input; 066 this.inputCharIndexed = RE.makeCharIndexed(input, 0); 067 } 068 069 /** 070 * @param sb The target string buffer 071 * @param replacement The replacement string 072 * 073 * @exception IllegalStateException If no match has yet been attempted, 074 * or if the previous match operation failed 075 * @exception IndexOutOfBoundsException If the replacement string refers 076 * to a capturing group that does not exist in the pattern 077 */ 078 public Matcher appendReplacement (StringBuffer sb, String replacement) 079 throws IllegalStateException 080 { 081 assertMatchOp(); 082 sb.append(input.subSequence(appendPosition, 083 match.getStartIndex()).toString()); 084 sb.append(RE.getReplacement(replacement, match, 085 RE.REG_REPLACE_USE_BACKSLASHESCAPE)); 086 appendPosition = match.getEndIndex(); 087 return this; 088 } 089 090 /** 091 * @param sb The target string buffer 092 */ 093 public StringBuffer appendTail (StringBuffer sb) 094 { 095 sb.append(input.subSequence(appendPosition, input.length()).toString()); 096 return sb; 097 } 098 099 /** 100 * @exception IllegalStateException If no match has yet been attempted, 101 * or if the previous match operation failed 102 */ 103 public int end () 104 throws IllegalStateException 105 { 106 assertMatchOp(); 107 return match.getEndIndex(); 108 } 109 110 /** 111 * @param group The index of a capturing group in this matcher's pattern 112 * 113 * @exception IllegalStateException If no match has yet been attempted, 114 * or if the previous match operation failed 115 * @exception IndexOutOfBoundsException If the replacement string refers 116 * to a capturing group that does not exist in the pattern 117 */ 118 public int end (int group) 119 throws IllegalStateException 120 { 121 assertMatchOp(); 122 return match.getEndIndex(group); 123 } 124 125 public boolean find () 126 { 127 boolean first = (match == null); 128 match = pattern.getRE().getMatch(inputCharIndexed, position); 129 if (match != null) 130 { 131 int endIndex = match.getEndIndex(); 132 // Are we stuck at the same position? 133 if (!first && endIndex == position) 134 { 135 match = null; 136 // Not at the end of the input yet? 137 if (position < input.length() - 1) 138 { 139 position++; 140 return find(position); 141 } 142 else 143 return false; 144 } 145 position = endIndex; 146 return true; 147 } 148 return false; 149 } 150 151 /** 152 * @param start The index to start the new pattern matching 153 * 154 * @exception IndexOutOfBoundsException If the replacement string refers 155 * to a capturing group that does not exist in the pattern 156 */ 157 public boolean find (int start) 158 { 159 match = pattern.getRE().getMatch(inputCharIndexed, start); 160 if (match != null) 161 { 162 position = match.getEndIndex(); 163 return true; 164 } 165 return false; 166 } 167 168 /** 169 * @exception IllegalStateException If no match has yet been attempted, 170 * or if the previous match operation failed 171 */ 172 public String group () 173 { 174 assertMatchOp(); 175 return match.toString(); 176 } 177 178 /** 179 * @param group The index of a capturing group in this matcher's pattern 180 * 181 * @exception IllegalStateException If no match has yet been attempted, 182 * or if the previous match operation failed 183 * @exception IndexOutOfBoundsException If the replacement string refers 184 * to a capturing group that does not exist in the pattern 185 */ 186 public String group (int group) 187 throws IllegalStateException 188 { 189 assertMatchOp(); 190 return match.toString(group); 191 } 192 193 /** 194 * @param replacement The replacement string 195 */ 196 public String replaceFirst (String replacement) 197 { 198 reset(); 199 // Semantics might not quite match 200 return pattern.getRE().substitute(input, replacement, position, 201 RE.REG_REPLACE_USE_BACKSLASHESCAPE); 202 } 203 204 /** 205 * @param replacement The replacement string 206 */ 207 public String replaceAll (String replacement) 208 { 209 reset(); 210 return pattern.getRE().substituteAll(input, replacement, position, 211 RE.REG_REPLACE_USE_BACKSLASHESCAPE); 212 } 213 214 public int groupCount () 215 { 216 return pattern.getRE().getNumSubs(); 217 } 218 219 public boolean lookingAt () 220 { 221 match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_FIX_STARTING_POSITION, null); 222 if (match != null) 223 { 224 if (match.getStartIndex() == 0) 225 { 226 position = match.getEndIndex(); 227 return true; 228 } 229 match = null; 230 } 231 return false; 232 } 233 234 /** 235 * Attempts to match the entire input sequence against the pattern. 236 * 237 * If the match succeeds then more information can be obtained via the 238 * start, end, and group methods. 239 * 240 * @see #start() 241 * @see #end() 242 * @see #group() 243 */ 244 public boolean matches () 245 { 246 match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_TRY_ENTIRE_MATCH|RE.REG_FIX_STARTING_POSITION, null); 247 if (match != null) 248 { 249 if (match.getStartIndex() == 0) 250 { 251 position = match.getEndIndex(); 252 if (position == input.length()) 253 return true; 254 } 255 match = null; 256 } 257 return false; 258 } 259 260 /** 261 * Returns the Pattern that is interpreted by this Matcher 262 */ 263 public Pattern pattern () 264 { 265 return pattern; 266 } 267 268 public Matcher reset () 269 { 270 position = 0; 271 match = null; 272 return this; 273 } 274 275 /** 276 * @param input The new input character sequence 277 */ 278 public Matcher reset (CharSequence input) 279 { 280 this.input = input; 281 this.inputCharIndexed = RE.makeCharIndexed(input, 0); 282 return reset(); 283 } 284 285 /** 286 * @returns the index of a capturing group in this matcher's pattern 287 * 288 * @exception IllegalStateException If no match has yet been attempted, 289 * or if the previous match operation failed 290 */ 291 public int start () 292 throws IllegalStateException 293 { 294 assertMatchOp(); 295 return match.getStartIndex(); 296 } 297 298 /** 299 * @param group The index of a capturing group in this matcher's pattern 300 * 301 * @exception IllegalStateException If no match has yet been attempted, 302 * or if the previous match operation failed 303 * @exception IndexOutOfBoundsException If the replacement string refers 304 * to a capturing group that does not exist in the pattern 305 */ 306 public int start (int group) 307 throws IllegalStateException 308 { 309 assertMatchOp(); 310 return match.getStartIndex(group); 311 } 312 313 /** 314 * @return True if and only if the matcher hit the end of input. 315 */ 316 public boolean hitEnd() 317 { 318 return inputCharIndexed.hitEnd(); 319 } 320 321 /** 322 * @return A string expression of this matcher. 323 */ 324 public String toString() 325 { 326 StringBuilder sb = new StringBuilder(); 327 sb.append(this.getClass().getName()) 328 .append("[pattern=").append(pattern.pattern()) 329 .append(" region=").append("0").append(",").append(input.length()) 330 .append(" lastmatch=").append(match == null ? "" : match.toString()) 331 .append("]"); 332 return sb.toString(); 333 } 334 335 private void assertMatchOp() 336 { 337 if (match == null) throw new IllegalStateException(); 338 } 339 }