001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.lookup; 018 019import java.util.Arrays; 020 021import org.apache.logging.log4j.util.Chars; 022import org.apache.logging.log4j.util.Strings; 023 024/** 025 * A matcher class that can be queried to determine if a character array 026 * portion matches. 027 * <p> 028 * This class comes complete with various factory methods. 029 * If these do not suffice, you can subclass and implement your own matcher. 030 */ 031public abstract class StrMatcher { 032 033 /** 034 * Matches the comma character. 035 */ 036 private static final StrMatcher COMMA_MATCHER = new CharMatcher(','); 037 /** 038 * Matches the tab character. 039 */ 040 private static final StrMatcher TAB_MATCHER = new CharMatcher(Chars.TAB); 041 /** 042 * Matches the space character. 043 */ 044 private static final StrMatcher SPACE_MATCHER = new CharMatcher(Chars.SPACE); 045 /** 046 * Matches the same characters as StringTokenizer, 047 * namely space, tab, newline, formfeed. 048 */ 049 private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray()); 050 /** 051 * Matches the String trim() whitespace characters. 052 */ 053 private static final StrMatcher TRIM_MATCHER = new TrimMatcher(); 054 /** 055 * Matches the double quote character. 056 */ 057 private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher(Chars.QUOTE); 058 /** 059 * Matches the double quote character. 060 */ 061 private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher(Chars.DQUOTE); 062 /** 063 * Matches the single or double quote character. 064 */ 065 private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray()); 066 /** 067 * Matches no characters. 068 */ 069 private static final StrMatcher NONE_MATCHER = new NoMatcher(); 070 071 /** 072 * Constructor. 073 */ 074 protected StrMatcher() { 075 } 076 077 /** 078 * Returns a matcher which matches the comma character. 079 * 080 * @return a matcher for a comma 081 */ 082 public static StrMatcher commaMatcher() { 083 return COMMA_MATCHER; 084 } 085 086 /** 087 * Returns a matcher which matches the tab character. 088 * 089 * @return a matcher for a tab 090 */ 091 public static StrMatcher tabMatcher() { 092 return TAB_MATCHER; 093 } 094 095 /** 096 * Returns a matcher which matches the space character. 097 * 098 * @return a matcher for a space 099 */ 100 public static StrMatcher spaceMatcher() { 101 return SPACE_MATCHER; 102 } 103 104 /** 105 * Matches the same characters as StringTokenizer, 106 * namely space, tab, newline and formfeed. 107 * 108 * @return the split matcher 109 */ 110 public static StrMatcher splitMatcher() { 111 return SPLIT_MATCHER; 112 } 113 114 /** 115 * Matches the String trim() whitespace characters. 116 * 117 * @return the trim matcher 118 */ 119 public static StrMatcher trimMatcher() { 120 return TRIM_MATCHER; 121 } 122 123 /** 124 * Returns a matcher which matches the single quote character. 125 * 126 * @return a matcher for a single quote 127 */ 128 public static StrMatcher singleQuoteMatcher() { 129 return SINGLE_QUOTE_MATCHER; 130 } 131 132 /** 133 * Returns a matcher which matches the double quote character. 134 * 135 * @return a matcher for a double quote 136 */ 137 public static StrMatcher doubleQuoteMatcher() { 138 return DOUBLE_QUOTE_MATCHER; 139 } 140 141 /** 142 * Returns a matcher which matches the single or double quote character. 143 * 144 * @return a matcher for a single or double quote 145 */ 146 public static StrMatcher quoteMatcher() { 147 return QUOTE_MATCHER; 148 } 149 150 /** 151 * Matches no characters. 152 * 153 * @return a matcher that matches nothing 154 */ 155 public static StrMatcher noneMatcher() { 156 return NONE_MATCHER; 157 } 158 159 /** 160 * Constructor that creates a matcher from a character. 161 * 162 * @param ch the character to match, must not be null 163 * @return a new Matcher for the given char 164 */ 165 public static StrMatcher charMatcher(final char ch) { 166 return new CharMatcher(ch); 167 } 168 169 /** 170 * Constructor that creates a matcher from a set of characters. 171 * 172 * @param chars the characters to match, null or empty matches nothing 173 * @return a new matcher for the given char[] 174 */ 175 public static StrMatcher charSetMatcher(final char[] chars) { 176 if (chars == null || chars.length == 0) { 177 return NONE_MATCHER; 178 } 179 if (chars.length == 1) { 180 return new CharMatcher(chars[0]); 181 } 182 return new CharSetMatcher(chars); 183 } 184 185 /** 186 * Constructor that creates a matcher from a string representing a set of characters. 187 * 188 * @param chars the characters to match, null or empty matches nothing 189 * @return a new Matcher for the given characters 190 */ 191 public static StrMatcher charSetMatcher(final String chars) { 192 if (Strings.isEmpty(chars)) { 193 return NONE_MATCHER; 194 } 195 if (chars.length() == 1) { 196 return new CharMatcher(chars.charAt(0)); 197 } 198 return new CharSetMatcher(chars.toCharArray()); 199 } 200 201 /** 202 * Constructor that creates a matcher from a string. 203 * 204 * @param str the string to match, null or empty matches nothing 205 * @return a new Matcher for the given String 206 */ 207 public static StrMatcher stringMatcher(final String str) { 208 if (Strings.isEmpty(str)) { 209 return NONE_MATCHER; 210 } 211 return new StringMatcher(str); 212 } 213 214 /** 215 * Returns the number of matching characters, zero for no match. 216 * <p> 217 * This method is called to check for a match. 218 * The parameter <code>pos</code> represents the current position to be 219 * checked in the string <code>buffer</code> (a character array which must 220 * not be changed). 221 * The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>. 222 * <p> 223 * The character array may be larger than the active area to be matched. 224 * Only values in the buffer between the specified indices may be accessed. 225 * <p> 226 * The matching code may check one character or many. 227 * It may check characters preceding <code>pos</code> as well as those 228 * after, so long as no checks exceed the bounds specified. 229 * <p> 230 * It must return zero for no match, or a positive number if a match was found. 231 * The number indicates the number of characters that matched. 232 * 233 * @param buffer the text content to match against, do not change 234 * @param pos the starting position for the match, valid for buffer 235 * @param bufferStart the first active index in the buffer, valid for buffer 236 * @param bufferEnd the end index (exclusive) of the active buffer, valid for buffer 237 * @return the number of matching characters, zero for no match 238 */ 239 public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd); 240 241 /** 242 * Returns the number of matching characters, zero for no match. 243 * <p> 244 * This method is called to check for a match. 245 * The parameter <code>pos</code> represents the current position to be 246 * checked in the string <code>buffer</code> (a character array which must 247 * not be changed). 248 * The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>. 249 * <p> 250 * The matching code may check one character or many. 251 * It may check characters preceding <code>pos</code> as well as those after. 252 * <p> 253 * It must return zero for no match, or a positive number if a match was found. 254 * The number indicates the number of characters that matched. 255 * 256 * @param buffer the text content to match against, do not change 257 * @param pos the starting position for the match, valid for buffer 258 * @return the number of matching characters, zero for no match 259 * @since 2.4 260 */ 261 public int isMatch(final char[] buffer, final int pos) { 262 return isMatch(buffer, pos, 0, buffer.length); 263 } 264 265 //----------------------------------------------------------------------- 266 /** 267 * Class used to define a set of characters for matching purposes. 268 */ 269 static final class CharSetMatcher extends StrMatcher { 270 /** The set of characters to match. */ 271 private final char[] chars; 272 273 /** 274 * Constructor that creates a matcher from a character array. 275 * 276 * @param chars the characters to match, must not be null 277 */ 278 CharSetMatcher(final char[] chars) { 279 super(); 280 this.chars = chars.clone(); 281 Arrays.sort(this.chars); 282 } 283 284 /** 285 * Returns whether or not the given character matches. 286 * 287 * @param buffer the text content to match against, do not change 288 * @param pos the starting position for the match, valid for buffer 289 * @param bufferStart the first active index in the buffer, valid for buffer 290 * @param bufferEnd the end index of the active buffer, valid for buffer 291 * @return the number of matching characters, zero for no match 292 */ 293 @Override 294 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { 295 return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0; 296 } 297 } 298 299 //----------------------------------------------------------------------- 300 /** 301 * Class used to define a character for matching purposes. 302 */ 303 static final class CharMatcher extends StrMatcher { 304 /** The character to match. */ 305 private final char ch; 306 307 /** 308 * Constructor that creates a matcher that matches a single character. 309 * 310 * @param ch the character to match 311 */ 312 CharMatcher(final char ch) { 313 super(); 314 this.ch = ch; 315 } 316 317 /** 318 * Returns whether or not the given character matches. 319 * 320 * @param buffer the text content to match against, do not change 321 * @param pos the starting position for the match, valid for buffer 322 * @param bufferStart the first active index in the buffer, valid for buffer 323 * @param bufferEnd the end index of the active buffer, valid for buffer 324 * @return the number of matching characters, zero for no match 325 */ 326 @Override 327 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { 328 return ch == buffer[pos] ? 1 : 0; 329 } 330 } 331 332 //----------------------------------------------------------------------- 333 /** 334 * Class used to define a set of characters for matching purposes. 335 */ 336 static final class StringMatcher extends StrMatcher { 337 /** The string to match, as a character array. */ 338 private final char[] chars; 339 340 /** 341 * Constructor that creates a matcher from a String. 342 * 343 * @param str the string to match, must not be null 344 */ 345 StringMatcher(final String str) { 346 super(); 347 chars = str.toCharArray(); 348 } 349 350 /** 351 * Returns whether or not the given text matches the stored string. 352 * 353 * @param buffer the text content to match against, do not change 354 * @param pos the starting position for the match, valid for buffer 355 * @param bufferStart the first active index in the buffer, valid for buffer 356 * @param bufferEnd the end index of the active buffer, valid for buffer 357 * @return the number of matching characters, zero for no match 358 */ 359 @Override 360 public int isMatch(final char[] buffer, int pos, final int bufferStart, final int bufferEnd) { 361 final int len = chars.length; 362 if (pos + len > bufferEnd) { 363 return 0; 364 } 365 for (int i = 0; i < chars.length; i++, pos++) { 366 if (chars[i] != buffer[pos]) { 367 return 0; 368 } 369 } 370 return len; 371 } 372 373 @Override 374 public String toString() { 375 return super.toString() + Chars.SPACE + Arrays.toString(chars); 376 } 377 378 } 379 380 //----------------------------------------------------------------------- 381 /** 382 * Class used to match no characters. 383 */ 384 static final class NoMatcher extends StrMatcher { 385 386 /** 387 * Constructs a new instance of <code>NoMatcher</code>. 388 */ 389 NoMatcher() { 390 super(); 391 } 392 393 /** 394 * Always returns {@code false}. 395 * 396 * @param buffer the text content to match against, do not change 397 * @param pos the starting position for the match, valid for buffer 398 * @param bufferStart the first active index in the buffer, valid for buffer 399 * @param bufferEnd the end index of the active buffer, valid for buffer 400 * @return the number of matching characters, zero for no match 401 */ 402 @Override 403 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { 404 return 0; 405 } 406 } 407 408 //----------------------------------------------------------------------- 409 /** 410 * Class used to match whitespace as per trim(). 411 */ 412 static final class TrimMatcher extends StrMatcher { 413 414 /** 415 * Constructs a new instance of <code>TrimMatcher</code>. 416 */ 417 TrimMatcher() { 418 super(); 419 } 420 421 /** 422 * Returns whether or not the given character matches. 423 * 424 * @param buffer the text content to match against, do not change 425 * @param pos the starting position for the match, valid for buffer 426 * @param bufferStart the first active index in the buffer, valid for buffer 427 * @param bufferEnd the end index of the active buffer, valid for buffer 428 * @return the number of matching characters, zero for no match 429 */ 430 @Override 431 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) { 432 return buffer[pos] <= ' ' ? 1 : 0; 433 } 434 } 435 436}