Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id D5432200D10 for ; Sat, 9 Sep 2017 17:14:05 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id D40331609B3; Sat, 9 Sep 2017 15:14:05 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 50A371609C4 for ; Sat, 9 Sep 2017 17:14:03 +0200 (CEST) Received: (qmail 36276 invoked by uid 500); 9 Sep 2017 15:13:57 -0000 Mailing-List: contact commits-help@hbase.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@hbase.apache.org Delivered-To: mailing list commits@hbase.apache.org Received: (qmail 33088 invoked by uid 99); 9 Sep 2017 15:13:55 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 09 Sep 2017 15:13:55 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id C593AF5728; Sat, 9 Sep 2017 15:13:54 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: git-site-role@apache.org To: commits@hbase.apache.org Date: Sat, 09 Sep 2017 15:14:19 -0000 Message-Id: <6bbc26de1052405d9df9177ebb8d9097@git.apache.org> In-Reply-To: <66995acc0cf745738afee05fa04155ea@git.apache.org> References: <66995acc0cf745738afee05fa04155ea@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [27/51] [partial] hbase-site git commit: Published site at . archived-at: Sat, 09 Sep 2017 15:14:06 -0000 http://git-wip-us.apache.org/repos/asf/hbase-site/blob/1490b3ab/apidocs/src-html/org/apache/hadoop/hbase/filter/ParseFilter.html ---------------------------------------------------------------------- diff --git a/apidocs/src-html/org/apache/hadoop/hbase/filter/ParseFilter.html b/apidocs/src-html/org/apache/hadoop/hbase/filter/ParseFilter.html index 8e035c5..b7697e0 100644 --- a/apidocs/src-html/org/apache/hadoop/hbase/filter/ParseFilter.html +++ b/apidocs/src-html/org/apache/hadoop/hbase/filter/ParseFilter.html @@ -40,842 +40,871 @@ 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; -035import org.apache.hadoop.hbase.classification.InterfaceAudience; -036import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; -037import org.apache.hadoop.hbase.util.Bytes; -038 -039/** -040 * This class allows a user to specify a filter via a string -041 * The string is parsed using the methods of this class and -042 * a filter object is constructed. This filter object is then wrapped -043 * in a scanner object which is then returned -044 * <p> -045 * This class addresses the HBASE-4168 JIRA. More documentation on this -046 * Filter Language can be found at: https://issues.apache.org/jira/browse/HBASE-4176 -047 */ -048@InterfaceAudience.Public -049public class ParseFilter { -050 private static final Log LOG = LogFactory.getLog(ParseFilter.class); -051 -052 private static HashMap<ByteBuffer, Integer> operatorPrecedenceHashMap; -053 private static HashMap<String, String> filterHashMap; -054 -055 static { -056 // Registers all the filter supported by the Filter Language -057 filterHashMap = new HashMap<>(); -058 filterHashMap.put("KeyOnlyFilter", ParseConstants.FILTER_PACKAGE + "." + -059 "KeyOnlyFilter"); -060 filterHashMap.put("FirstKeyOnlyFilter", ParseConstants.FILTER_PACKAGE + "." + -061 "FirstKeyOnlyFilter"); -062 filterHashMap.put("PrefixFilter", ParseConstants.FILTER_PACKAGE + "." + -063 "PrefixFilter"); -064 filterHashMap.put("ColumnPrefixFilter", ParseConstants.FILTER_PACKAGE + "." + -065 "ColumnPrefixFilter"); -066 filterHashMap.put("MultipleColumnPrefixFilter", ParseConstants.FILTER_PACKAGE + "." + -067 "MultipleColumnPrefixFilter"); -068 filterHashMap.put("ColumnCountGetFilter", ParseConstants.FILTER_PACKAGE + "." + -069 "ColumnCountGetFilter"); -070 filterHashMap.put("PageFilter", ParseConstants.FILTER_PACKAGE + "." + -071 "PageFilter"); -072 filterHashMap.put("ColumnPaginationFilter", ParseConstants.FILTER_PACKAGE + "." + -073 "ColumnPaginationFilter"); -074 filterHashMap.put("InclusiveStopFilter", ParseConstants.FILTER_PACKAGE + "." + -075 "InclusiveStopFilter"); -076 filterHashMap.put("TimestampsFilter", ParseConstants.FILTER_PACKAGE + "." + -077 "TimestampsFilter"); -078 filterHashMap.put("RowFilter", ParseConstants.FILTER_PACKAGE + "." + -079 "RowFilter"); -080 filterHashMap.put("FamilyFilter", ParseConstants.FILTER_PACKAGE + "." + -081 "FamilyFilter"); -082 filterHashMap.put("QualifierFilter", ParseConstants.FILTER_PACKAGE + "." + -083 "QualifierFilter"); -084 filterHashMap.put("ValueFilter", ParseConstants.FILTER_PACKAGE + "." + -085 "ValueFilter"); -086 filterHashMap.put("ColumnRangeFilter", ParseConstants.FILTER_PACKAGE + "." + -087 "ColumnRangeFilter"); -088 filterHashMap.put("SingleColumnValueFilter", ParseConstants.FILTER_PACKAGE + "." + -089 "SingleColumnValueFilter"); -090 filterHashMap.put("SingleColumnValueExcludeFilter", ParseConstants.FILTER_PACKAGE + "." + -091 "SingleColumnValueExcludeFilter"); -092 filterHashMap.put("DependentColumnFilter", ParseConstants.FILTER_PACKAGE + "." + -093 "DependentColumnFilter"); -094 -095 // Creates the operatorPrecedenceHashMap -096 operatorPrecedenceHashMap = new HashMap<>(); -097 operatorPrecedenceHashMap.put(ParseConstants.SKIP_BUFFER, 1); -098 operatorPrecedenceHashMap.put(ParseConstants.WHILE_BUFFER, 1); -099 operatorPrecedenceHashMap.put(ParseConstants.AND_BUFFER, 2); -100 operatorPrecedenceHashMap.put(ParseConstants.OR_BUFFER, 3); -101 } -102 -103 /** -104 * Parses the filterString and constructs a filter using it -105 * <p> -106 * @param filterString filter string given by the user -107 * @return filter object we constructed -108 */ -109 public Filter parseFilterString (String filterString) -110 throws CharacterCodingException { -111 return parseFilterString(Bytes.toBytes(filterString)); -112 } -113 -114 /** -115 * Parses the filterString and constructs a filter using it -116 * <p> -117 * @param filterStringAsByteArray filter string given by the user -118 * @return filter object we constructed -119 */ -120 public Filter parseFilterString (byte [] filterStringAsByteArray) -121 throws CharacterCodingException { -122 // stack for the operators and parenthesis -123 Stack <ByteBuffer> operatorStack = new Stack<>(); -124 // stack for the filter objects -125 Stack <Filter> filterStack = new Stack<>(); -126 -127 Filter filter = null; -128 for (int i=0; i<filterStringAsByteArray.length; i++) { -129 if (filterStringAsByteArray[i] == ParseConstants.LPAREN) { -130 // LPAREN found -131 operatorStack.push(ParseConstants.LPAREN_BUFFER); -132 } else if (filterStringAsByteArray[i] == ParseConstants.WHITESPACE || -133 filterStringAsByteArray[i] == ParseConstants.TAB) { -134 // WHITESPACE or TAB found -135 continue; -136 } else if (checkForOr(filterStringAsByteArray, i)) { -137 // OR found -138 i += ParseConstants.OR_ARRAY.length - 1; -139 reduce(operatorStack, filterStack, ParseConstants.OR_BUFFER); -140 operatorStack.push(ParseConstants.OR_BUFFER); -141 } else if (checkForAnd(filterStringAsByteArray, i)) { -142 // AND found -143 i += ParseConstants.AND_ARRAY.length - 1; -144 reduce(operatorStack, filterStack, ParseConstants.AND_BUFFER); -145 operatorStack.push(ParseConstants.AND_BUFFER); -146 } else if (checkForSkip(filterStringAsByteArray, i)) { -147 // SKIP found -148 i += ParseConstants.SKIP_ARRAY.length - 1; -149 reduce(operatorStack, filterStack, ParseConstants.SKIP_BUFFER); -150 operatorStack.push(ParseConstants.SKIP_BUFFER); -151 } else if (checkForWhile(filterStringAsByteArray, i)) { -152 // WHILE found -153 i += ParseConstants.WHILE_ARRAY.length - 1; -154 reduce(operatorStack, filterStack, ParseConstants.WHILE_BUFFER); -155 operatorStack.push(ParseConstants.WHILE_BUFFER); -156 } else if (filterStringAsByteArray[i] == ParseConstants.RPAREN) { -157 // RPAREN found -158 if (operatorStack.empty()) { -159 throw new IllegalArgumentException("Mismatched parenthesis"); -160 } -161 ByteBuffer argumentOnTopOfStack = operatorStack.peek(); -162 if (argumentOnTopOfStack.equals(ParseConstants.LPAREN_BUFFER)) { -163 operatorStack.pop(); -164 continue; -165 } -166 while (!(argumentOnTopOfStack.equals(ParseConstants.LPAREN_BUFFER))) { -167 filterStack.push(popArguments(operatorStack, filterStack)); -168 if (operatorStack.empty()) { -169 throw new IllegalArgumentException("Mismatched parenthesis"); -170 } -171 argumentOnTopOfStack = operatorStack.pop(); -172 } -173 } else { -174 // SimpleFilterExpression found -175 byte [] filterSimpleExpression = extractFilterSimpleExpression(filterStringAsByteArray, i); -176 i+= (filterSimpleExpression.length - 1); -177 filter = parseSimpleFilterExpression(filterSimpleExpression); -178 filterStack.push(filter); -179 } -180 } -181 -182 // Finished parsing filterString -183 while (!operatorStack.empty()) { -184 filterStack.push(popArguments(operatorStack, filterStack)); -185 } -186 if (filterStack.empty()) { -187 throw new IllegalArgumentException("Incorrect Filter String"); -188 } -189 filter = filterStack.pop(); -190 if (!filterStack.empty()) { -191 throw new IllegalArgumentException("Incorrect Filter String"); -192 } -193 return filter; -194 } -195 -196/** -197 * Extracts a simple filter expression from the filter string given by the user -198 * <p> -199 * A simpleFilterExpression is of the form: FilterName('arg', 'arg', 'arg') -200 * The user given filter string can have many simpleFilterExpressions combined -201 * using operators. -202 * <p> -203 * This function extracts a simpleFilterExpression from the -204 * larger filterString given the start offset of the simpler expression -205 * <p> -206 * @param filterStringAsByteArray filter string given by the user -207 * @param filterExpressionStartOffset start index of the simple filter expression -208 * @return byte array containing the simple filter expression -209 */ -210 public byte [] extractFilterSimpleExpression (byte [] filterStringAsByteArray, -211 int filterExpressionStartOffset) -212 throws CharacterCodingException { -213 int quoteCount = 0; -214 for (int i=filterExpressionStartOffset; i<filterStringAsByteArray.length; i++) { -215 if (filterStringAsByteArray[i] == ParseConstants.SINGLE_QUOTE) { -216 if (isQuoteUnescaped(filterStringAsByteArray, i)) { -217 quoteCount ++; -218 } else { -219 // To skip the next quote that has been escaped -220 i++; -221 } -222 } -223 if (filterStringAsByteArray[i] == ParseConstants.RPAREN && (quoteCount %2 ) == 0) { -224 byte [] filterSimpleExpression = new byte [i - filterExpressionStartOffset + 1]; -225 Bytes.putBytes(filterSimpleExpression, 0, filterStringAsByteArray, -226 filterExpressionStartOffset, i-filterExpressionStartOffset + 1); -227 return filterSimpleExpression; -228 } -229 } -230 throw new IllegalArgumentException("Incorrect Filter String"); -231 } -232 -233/** -234 * Constructs a filter object given a simple filter expression -235 * <p> -236 * @param filterStringAsByteArray filter string given by the user -237 * @return filter object we constructed -238 */ -239 public Filter parseSimpleFilterExpression (byte [] filterStringAsByteArray) -240 throws CharacterCodingException { -241 -242 String filterName = Bytes.toString(getFilterName(filterStringAsByteArray)); -243 ArrayList<byte []> filterArguments = getFilterArguments(filterStringAsByteArray); -244 if (!filterHashMap.containsKey(filterName)) { -245 throw new IllegalArgumentException("Filter Name " + filterName + " not supported"); -246 } -247 try { -248 filterName = filterHashMap.get(filterName); -249 Class<?> c = Class.forName(filterName); -250 Class<?>[] argTypes = new Class [] {ArrayList.class}; -251 Method m = c.getDeclaredMethod("createFilterFromArguments", argTypes); -252 return (Filter) m.invoke(null,filterArguments); -253 } catch (ClassNotFoundException e) { -254 e.printStackTrace(); -255 } catch (NoSuchMethodException e) { -256 e.printStackTrace(); -257 } catch (IllegalAccessException e) { -258 e.printStackTrace(); -259 } catch (InvocationTargetException e) { -260 e.printStackTrace(); -261 } -262 throw new IllegalArgumentException("Incorrect filter string " + -263 new String(filterStringAsByteArray)); -264 } -265 -266/** -267 * Returns the filter name given a simple filter expression -268 * <p> -269 * @param filterStringAsByteArray a simple filter expression -270 * @return name of filter in the simple filter expression -271 */ -272 public static byte [] getFilterName (byte [] filterStringAsByteArray) { -273 int filterNameStartIndex = 0; -274 int filterNameEndIndex = 0; -275 -276 for (int i=filterNameStartIndex; i<filterStringAsByteArray.length; i++) { -277 if (filterStringAsByteArray[i] == ParseConstants.LPAREN || -278 filterStringAsByteArray[i] == ParseConstants.WHITESPACE) { -279 filterNameEndIndex = i; -280 break; -281 } -282 } -283 -284 if (filterNameEndIndex == 0) { -285 throw new IllegalArgumentException("Incorrect Filter Name"); -286 } -287 -288 byte [] filterName = new byte[filterNameEndIndex - filterNameStartIndex]; -289 Bytes.putBytes(filterName, 0, filterStringAsByteArray, 0, -290 filterNameEndIndex - filterNameStartIndex); -291 return filterName; -292 } -293 -294/** -295 * Returns the arguments of the filter from the filter string -296 * <p> -297 * @param filterStringAsByteArray filter string given by the user -298 * @return an ArrayList containing the arguments of the filter in the filter string -299 */ -300 public static ArrayList<byte []> getFilterArguments (byte [] filterStringAsByteArray) { -301 int argumentListStartIndex = Bytes.searchDelimiterIndex(filterStringAsByteArray, 0, -302 filterStringAsByteArray.length, -303 ParseConstants.LPAREN); -304 if (argumentListStartIndex == -1) { -305 throw new IllegalArgumentException("Incorrect argument list"); -306 } -307 -308 int argumentStartIndex = 0; -309 int argumentEndIndex = 0; -310 ArrayList<byte []> filterArguments = new ArrayList<>(); -311 -312 for (int i = argumentListStartIndex + 1; i<filterStringAsByteArray.length; i++) { -313 -314 if (filterStringAsByteArray[i] == ParseConstants.WHITESPACE || -315 filterStringAsByteArray[i] == ParseConstants.COMMA || -316 filterStringAsByteArray[i] == ParseConstants.RPAREN) { -317 continue; -318 } -319 -320 // The argument is in single quotes - for example 'prefix' -321 if (filterStringAsByteArray[i] == ParseConstants.SINGLE_QUOTE) { -322 argumentStartIndex = i; -323 for (int j = argumentStartIndex+1; j < filterStringAsByteArray.length; j++) { -324 if (filterStringAsByteArray[j] == ParseConstants.SINGLE_QUOTE) { -325 if (isQuoteUnescaped(filterStringAsByteArray,j)) { -326 argumentEndIndex = j; -327 i = j+1; -328 byte [] filterArgument = createUnescapdArgument(filterStringAsByteArray, -329 argumentStartIndex, argumentEndIndex); -330 filterArguments.add(filterArgument); -331 break; -332 } else { -333 // To jump over the second escaped quote -334 j++; -335 } -336 } else if (j == filterStringAsByteArray.length - 1) { -337 throw new IllegalArgumentException("Incorrect argument list"); -338 } -339 } -340 } else { -341 // The argument is an integer, boolean, comparison operator like <, >, != etc -342 argumentStartIndex = i; -343 for (int j = argumentStartIndex; j < filterStringAsByteArray.length; j++) { -344 if (filterStringAsByteArray[j] == ParseConstants.WHITESPACE || -345 filterStringAsByteArray[j] == ParseConstants.COMMA || -346 filterStringAsByteArray[j] == ParseConstants.RPAREN) { -347 argumentEndIndex = j - 1; -348 i = j; -349 byte [] filterArgument = new byte [argumentEndIndex - argumentStartIndex + 1]; -350 Bytes.putBytes(filterArgument, 0, filterStringAsByteArray, -351 argumentStartIndex, argumentEndIndex - argumentStartIndex + 1); -352 filterArguments.add(filterArgument); -353 break; -354 } else if (j == filterStringAsByteArray.length - 1) { -355 throw new IllegalArgumentException("Incorrect argument list"); -356 } -357 } -358 } -359 } -360 return filterArguments; -361 } -362 -363/** -364 * This function is called while parsing the filterString and an operator is parsed -365 * <p> -366 * @param operatorStack the stack containing the operators and parenthesis -367 * @param filterStack the stack containing the filters -368 * @param operator the operator found while parsing the filterString -369 */ -370 public void reduce(Stack<ByteBuffer> operatorStack, -371 Stack<Filter> filterStack, -372 ByteBuffer operator) { -373 while (!operatorStack.empty() && -374 !(ParseConstants.LPAREN_BUFFER.equals(operatorStack.peek())) && -375 hasHigherPriority(operatorStack.peek(), operator)) { -376 filterStack.push(popArguments(operatorStack, filterStack)); -377 } -378 } -379 -380 /** -381 * Pops an argument from the operator stack and the number of arguments required by the operator -382 * from the filterStack and evaluates them -383 * <p> -384 * @param operatorStack the stack containing the operators -385 * @param filterStack the stack containing the filters -386 * @return the evaluated filter -387 */ -388 public static Filter popArguments (Stack<ByteBuffer> operatorStack, Stack <Filter> filterStack) { -389 ByteBuffer argumentOnTopOfStack = operatorStack.peek(); -390 -391 if (argumentOnTopOfStack.equals(ParseConstants.OR_BUFFER)) { -392 // The top of the stack is an OR -393 try { -394 ArrayList<Filter> listOfFilters = new ArrayList<>(); -395 while (!operatorStack.empty() && operatorStack.peek().equals(ParseConstants.OR_BUFFER)) { -396 Filter filter = filterStack.pop(); -397 listOfFilters.add(0, filter); -398 operatorStack.pop(); -399 } -400 Filter filter = filterStack.pop(); -401 listOfFilters.add(0, filter); -402 Filter orFilter = new FilterList(FilterList.Operator.MUST_PASS_ONE, listOfFilters); -403 return orFilter; -404 } catch (EmptyStackException e) { -405 throw new IllegalArgumentException("Incorrect input string - an OR needs two filters"); -406 } -407 -408 } else if (argumentOnTopOfStack.equals(ParseConstants.AND_BUFFER)) { -409 // The top of the stack is an AND -410 try { -411 ArrayList<Filter> listOfFilters = new ArrayList<>(); -412 while (!operatorStack.empty() && operatorStack.peek().equals(ParseConstants.AND_BUFFER)) { -413 Filter filter = filterStack.pop(); -414 listOfFilters.add(0, filter); -415 operatorStack.pop(); -416 } -417 Filter filter = filterStack.pop(); -418 listOfFilters.add(0, filter); -419 Filter andFilter = new FilterList(FilterList.Operator.MUST_PASS_ALL, listOfFilters); -420 return andFilter; -421 } catch (EmptyStackException e) { -422 throw new IllegalArgumentException("Incorrect input string - an AND needs two filters"); -423 } -424 -425 } else if (argumentOnTopOfStack.equals(ParseConstants.SKIP_BUFFER)) { -426 // The top of the stack is a SKIP -427 try { -428 Filter wrappedFilter = filterStack.pop(); -429 Filter skipFilter = new SkipFilter(wrappedFilter); -430 operatorStack.pop(); -431 return skipFilter; -432 } catch (EmptyStackException e) { -433 throw new IllegalArgumentException("Incorrect input string - a SKIP wraps a filter"); -434 } -435 -436 } else if (argumentOnTopOfStack.equals(ParseConstants.WHILE_BUFFER)) { -437 // The top of the stack is a WHILE -438 try { -439 Filter wrappedFilter = filterStack.pop(); -440 Filter whileMatchFilter = new WhileMatchFilter(wrappedFilter); -441 operatorStack.pop(); -442 return whileMatchFilter; -443 } catch (EmptyStackException e) { -444 throw new IllegalArgumentException("Incorrect input string - a WHILE wraps a filter"); -445 } -446 -447 } else if (argumentOnTopOfStack.equals(ParseConstants.LPAREN_BUFFER)) { -448 // The top of the stack is a LPAREN -449 try { -450 Filter filter = filterStack.pop(); -451 operatorStack.pop(); -452 return filter; -453 } catch (EmptyStackException e) { -454 throw new IllegalArgumentException("Incorrect Filter String"); -455 } -456 -457 } else { -458 throw new IllegalArgumentException("Incorrect arguments on operatorStack"); -459 } -460 } -461 -462/** -463 * Returns which operator has higher precedence -464 * <p> -465 * If a has higher precedence than b, it returns true -466 * If they have the same precedence, it returns false -467 */ -468 public boolean hasHigherPriority(ByteBuffer a, ByteBuffer b) { -469 if ((operatorPrecedenceHashMap.get(a) - operatorPrecedenceHashMap.get(b)) < 0) { -470 return true; -471 } -472 return false; -473 } -474 -475/** -476 * Removes the single quote escaping a single quote - thus it returns an unescaped argument -477 * <p> -478 * @param filterStringAsByteArray filter string given by user -479 * @param argumentStartIndex start index of the argument -480 * @param argumentEndIndex end index of the argument -481 * @return returns an unescaped argument -482 */ -483 public static byte [] createUnescapdArgument (byte [] filterStringAsByteArray, -484 int argumentStartIndex, int argumentEndIndex) { -485 int unescapedArgumentLength = 2; -486 for (int i = argumentStartIndex + 1; i <= argumentEndIndex - 1; i++) { -487 unescapedArgumentLength ++; -488 if (filterStringAsByteArray[i] == ParseConstants.SINGLE_QUOTE && -489 i != (argumentEndIndex - 1) && -490 filterStringAsByteArray[i+1] == ParseConstants.SINGLE_QUOTE) { -491 i++; -492 continue; -493 } -494 } -495 -496 byte [] unescapedArgument = new byte [unescapedArgumentLength]; -497 int count = 1; -498 unescapedArgument[0] = '\''; -499 for (int i = argumentStartIndex + 1; i <= argumentEndIndex - 1; i++) { -500 if (filterStringAsByteArray [i] == ParseConstants.SINGLE_QUOTE && -501 i != (argumentEndIndex - 1) && -502 filterStringAsByteArray [i+1] == ParseConstants.SINGLE_QUOTE) { -503 unescapedArgument[count++] = filterStringAsByteArray [i+1]; -504 i++; -505 } -506 else { -507 unescapedArgument[count++] = filterStringAsByteArray [i]; -508 } -509 } -510 unescapedArgument[unescapedArgumentLength - 1] = '\''; -511 return unescapedArgument; -512 } -513 -514/** -515 * Checks if the current index of filter string we are on is the beginning of the keyword 'OR' -516 * <p> -517 * @param filterStringAsByteArray filter string given by the user -518 * @param indexOfOr index at which an 'O' was read -519 * @return true if the keyword 'OR' is at the current index -520 */ -521 public static boolean checkForOr (byte [] filterStringAsByteArray, int indexOfOr) -522 throws CharacterCodingException, ArrayIndexOutOfBoundsException { -523 -524 try { -525 if (filterStringAsByteArray[indexOfOr] == ParseConstants.O && -526 filterStringAsByteArray[indexOfOr+1] == ParseConstants.R && -527 (filterStringAsByteArray[indexOfOr-1] == ParseConstants.WHITESPACE || -528 filterStringAsByteArray[indexOfOr-1] == ParseConstants.RPAREN) && -529 (filterStringAsByteArray[indexOfOr+2] == ParseConstants.WHITESPACE || -530 filterStringAsByteArray[indexOfOr+2] == ParseConstants.LPAREN)) { -531 return true; -532 } else { -533 return false; -534 } -535 } catch (ArrayIndexOutOfBoundsException e) { -536 return false; -537 } -538 } -539 -540/** -541 * Checks if the current index of filter string we are on is the beginning of the keyword 'AND' -542 * <p> -543 * @param filterStringAsByteArray filter string given by the user -544 * @param indexOfAnd index at which an 'A' was read -545 * @return true if the keyword 'AND' is at the current index -546 */ -547 public static boolean checkForAnd (byte [] filterStringAsByteArray, int indexOfAnd) -548 throws CharacterCodingException { -549 -550 try { -551 if (filterStringAsByteArray[indexOfAnd] == ParseConstants.A && -552 filterStringAsByteArray[indexOfAnd+1] == ParseConstants.N && -553 filterStringAsByteArray[indexOfAnd+2] == ParseConstants.D && -554 (filterStringAsByteArray[indexOfAnd-1] == ParseConstants.WHITESPACE || -555 filterStringAsByteArray[indexOfAnd-1] == ParseConstants.RPAREN) && -556 (filterStringAsByteArray[indexOfAnd+3] == ParseConstants.WHITESPACE || -557 filterStringAsByteArray[indexOfAnd+3] == ParseConstants.LPAREN)) { -558 return true; -559 } else { -560 return false; -561 } -562 } catch (ArrayIndexOutOfBoundsException e) { -563 return false; -564 } -565 } -566 -567/** -568 * Checks if the current index of filter string we are on is the beginning of the keyword 'SKIP' -569 * <p> -570 * @param filterStringAsByteArray filter string given by the user -571 * @param indexOfSkip index at which an 'S' was read -572 * @return true if the keyword 'SKIP' is at the current index -573 */ -574 public static boolean checkForSkip (byte [] filterStringAsByteArray, int indexOfSkip) -575 throws CharacterCodingException { -576 -577 try { -578 if (filterStringAsByteArray[indexOfSkip] == ParseConstants.S && -579 filterStringAsByteArray[indexOfSkip+1] == ParseConstants.K && -580 filterStringAsByteArray[indexOfSkip+2] == ParseConstants.I && -581 filterStringAsByteArray[indexOfSkip+3] == ParseConstants.P && -582 (indexOfSkip == 0 || -583 filterStringAsByteArray[indexOfSkip-1] == ParseConstants.WHITESPACE || -584 filterStringAsByteArray[indexOfSkip-1] == ParseConstants.RPAREN || -585 filterStringAsByteArray[indexOfSkip-1] == ParseConstants.LPAREN) && -586 (filterStringAsByteArray[indexOfSkip+4] == ParseConstants.WHITESPACE || -587 filterStringAsByteArray[indexOfSkip+4] == ParseConstants.LPAREN)) { -588 return true; -589 } else { -590 return false; -591 } -592 } catch (ArrayIndexOutOfBoundsException e) { -593 return false; -594 } -595 } -596 -597/** -598 * Checks if the current index of filter string we are on is the beginning of the keyword 'WHILE' -599 * <p> -600 * @param filterStringAsByteArray filter string given by the user -601 * @param indexOfWhile index at which an 'W' was read -602 * @return true if the keyword 'WHILE' is at the current index -603 */ -604 public static boolean checkForWhile (byte [] filterStringAsByteArray, int indexOfWhile) -605 throws CharacterCodingException { -606 -607 try { -608 if (filterStringAsByteArray[indexOfWhile] == ParseConstants.W && -609 filterStringAsByteArray[indexOfWhile+1] == ParseConstants.H && -610 filterStringAsByteArray[indexOfWhile+2] == ParseConstants.I && -611 filterStringAsByteArray[indexOfWhile+3] == ParseConstants.L && -612 filterStringAsByteArray[indexOfWhile+4] == ParseConstants.E && -613 (indexOfWhile == 0 || filterStringAsByteArray[indexOfWhile-1] == ParseConstants.WHITESPACE -614 || filterStringAsByteArray[indexOfWhile-1] == ParseConstants.RPAREN || -615 filterStringAsByteArray[indexOfWhile-1] == ParseConstants.LPAREN) && -616 (filterStringAsByteArray[indexOfWhile+5] == ParseConstants.WHITESPACE || -617 filterStringAsByteArray[indexOfWhile+5] == ParseConstants.LPAREN)) { -618 return true; -619 } else { -620 return false; -621 } -622 } catch (ArrayIndexOutOfBoundsException e) { -623 return false; -624 } -625 } -626 -627/** -628 * Returns a boolean indicating whether the quote was escaped or not -629 * <p> -630 * @param array byte array in which the quote was found -631 * @param quoteIndex index of the single quote -632 * @return returns true if the quote was unescaped -633 */ -634 public static boolean isQuoteUnescaped (byte [] array, int quoteIndex) { -635 if (array == null) { -636 throw new IllegalArgumentException("isQuoteUnescaped called with a null array"); -637 } -638 -639 if (quoteIndex == array.length - 1 || array[quoteIndex+1] != ParseConstants.SINGLE_QUOTE) { -640 return true; -641 } -642 else { -643 return false; -644 } -645 } -646 -647/** -648 * Takes a quoted byte array and converts it into an unquoted byte array -649 * For example: given a byte array representing 'abc', it returns a -650 * byte array representing abc -651 * <p> -652 * @param quotedByteArray the quoted byte array -653 * @return Unquoted byte array -654 */ -655 public static byte [] removeQuotesFromByteArray (byte [] quotedByteArray) { -656 if (quotedByteArray == null || -657 quotedByteArray.length < 2 || -658 quotedByteArray[0] != ParseConstants.SINGLE_QUOTE || -659 quotedByteArray[quotedByteArray.length - 1] != ParseConstants.SINGLE_QUOTE) { -660 throw new IllegalArgumentException("removeQuotesFromByteArray needs a quoted byte array"); -661 } else { -662 byte [] targetString = new byte [quotedByteArray.length - 2]; -663 Bytes.putBytes(targetString, 0, quotedByteArray, 1, quotedByteArray.length - 2); -664 return targetString; -665 } -666 } -667 -668/** -669 * Converts an int expressed in a byte array to an actual int -670 * <p> -671 * This doesn't use Bytes.toInt because that assumes -672 * that there will be {@link Bytes#SIZEOF_INT} bytes available. -673 * <p> -674 * @param numberAsByteArray the int value expressed as a byte array -675 * @return the int value -676 */ -677 public static int convertByteArrayToInt (byte [] numberAsByteArray) { -678 -679 long tempResult = ParseFilter.convertByteArrayToLong(numberAsByteArray); -680 -681 if (tempResult > Integer.MAX_VALUE) { -682 throw new IllegalArgumentException("Integer Argument too large"); -683 } else if (tempResult < Integer.MIN_VALUE) { -684 throw new IllegalArgumentException("Integer Argument too small"); -685 } -686 -687 int result = (int) tempResult; -688 return result; -689 } -690 -691/** -692 * Converts a long expressed in a byte array to an actual long -693 * <p> -694 * This doesn't use Bytes.toLong because that assumes -695 * that there will be {@link Bytes#SIZEOF_INT} bytes available. -696 * <p> -697 * @param numberAsByteArray the long value expressed as a byte array -698 * @return the long value -699 */ -700 public static long convertByteArrayToLong (byte [] numberAsByteArray) { -701 if (numberAsByteArray == null) { -702 throw new IllegalArgumentException("convertByteArrayToLong called with a null array"); -703 } -704 -705 int i = 0; -706 long result = 0; -707 boolean isNegative = false; -708 -709 if (numberAsByteArray[i] == ParseConstants.MINUS_SIGN) { -710 i++; -711 isNegative = true; -712 } -713 -714 while (i != numberAsByteArray.length) { -715 if (numberAsByteArray[i] < ParseConstants.ZERO || -716 numberAsByteArray[i] > ParseConstants.NINE) { -717 throw new IllegalArgumentException("Byte Array should only contain digits"); -718 } -719 result = result*10 + (numberAsByteArray[i] - ParseConstants.ZERO); -720 if (result < 0) { -721 throw new IllegalArgumentException("Long Argument too large"); -722 } -723 i++; -724 } -725 -726 if (isNegative) { -727 return -result; -728 } else { -729 return result; -730 } -731 } -732 -733/** -734 * Converts a boolean expressed in a byte array to an actual boolean -735 *<p> -736 * This doesn't used Bytes.toBoolean because Bytes.toBoolean(byte []) -737 * assumes that 1 stands for true and 0 for false. -738 * Here, the byte array representing "true" and "false" is parsed -739 * <p> -740 * @param booleanAsByteArray the boolean value expressed as a byte array -741 * @return the boolean value -742 */ -743 public static boolean convertByteArrayToBoolean (byte [] booleanAsByteArray) { -744 if (booleanAsByteArray == null) { -745 throw new IllegalArgumentException("convertByteArrayToBoolean called with a null array"); -746 } -747 -748 if (booleanAsByteArray.length == 4 && -749 (booleanAsByteArray[0] == 't' || booleanAsByteArray[0] == 'T') && -750 (booleanAsByteArray[1] == 'r' || booleanAsByteArray[1] == 'R') && -751 (booleanAsByteArray[2] == 'u' || booleanAsByteArray[2] == 'U') && -752 (booleanAsByteArray[3] == 'e' || booleanAsByteArray[3] == 'E')) { -753 return true; -754 } -755 else if (booleanAsByteArray.length == 5 && -756 (booleanAsByteArray[0] == 'f' || booleanAsByteArray[0] == 'F') && -757 (booleanAsByteArray[1] == 'a' || booleanAsByteArray[1] == 'A') && -758 (booleanAsByteArray[2] == 'l' || booleanAsByteArray[2] == 'L') && -759 (booleanAsByteArray[3] == 's' || booleanAsByteArray[3] == 'S') && -760 (booleanAsByteArray[4] == 'e' || booleanAsByteArray[4] == 'E')) { -761 return false; -762 } -763 else { -764 throw new IllegalArgumentException("Incorrect Boolean Expression"); -765 } -766 } -767 -768/** -769 * Takes a compareOperator symbol as a byte array and returns the corresponding CompareOperator -770 * <p> -771 * @param compareOpAsByteArray the comparatorOperator symbol as a byte array -772 * @return the Compare Operator -773 */ -774 public static CompareFilter.CompareOp createCompareOp (byte [] compareOpAsByteArray) { -775 ByteBuffer compareOp = ByteBuffer.wrap(compareOpAsByteArray); -776 if (compareOp.equals(ParseConstants.LESS_THAN_BUFFER)) -777 return CompareOp.LESS; -778 else if (compareOp.equals(ParseConstants.LESS_THAN_OR_EQUAL_TO_BUFFER)) -779 return CompareOp.LESS_OR_EQUAL; -780 else if (compareOp.equals(ParseConstants.GREATER_THAN_BUFFER)) -781 return CompareOp.GREATER; -782 else if (compareOp.equals(ParseConstants.GREATER_THAN_OR_EQUAL_TO_BUFFER)) -783 return CompareOp.GREATER_OR_EQUAL; -784 else if (compareOp.equals(ParseConstants.NOT_EQUAL_TO_BUFFER)) -785 return CompareOp.NOT_EQUAL; -786 else if (compareOp.equals(ParseConstants.EQUAL_TO_BUFFER)) -787 return CompareOp.EQUAL; -788 else -789 throw new IllegalArgumentException("Invalid compare operator"); -790 } -791 -792/** -793 * Parses a comparator of the form comparatorType:comparatorValue form and returns a comparator -794 * <p> -795 * @param comparator the comparator in the form comparatorType:comparatorValue -796 * @return the parsed comparator -797 */ -798 public static ByteArrayComparable createComparator (byte [] comparator) { -799 if (comparator == null) -800 throw new IllegalArgumentException("Incorrect Comparator"); -801 byte [][] parsedComparator = ParseFilter.parseComparator(comparator); -802 byte [] comparatorType = parsedComparator[0]; -803 byte [] comparatorValue = parsedComparator[1]; -804 -805 -806 if (Bytes.equals(comparatorType, ParseConstants.binaryType)) -807 return new BinaryComparator(comparatorValue); -808 else if (Bytes.equals(comparatorType, ParseConstants.binaryPrefixType)) -809 return new BinaryPrefixComparator(comparatorValue); -810 else if (Bytes.equals(comparatorType, ParseConstants.regexStringType)) -811 return new RegexStringComparator(new String(comparatorValue)); -812 else if (Bytes.equals(comparatorType, ParseConstants.substringType)) -813 return new SubstringComparator(new String(comparatorValue)); -814 else -815 throw new IllegalArgumentException("Incorrect comparatorType"); -816 } -817 -818/** -819 * Splits a column in comparatorType:comparatorValue form into separate byte arrays -820 * <p> -821 * @param comparator the comparator -822 * @return the parsed arguments of the comparator as a 2D byte array -823 */ -824 public static byte [][] parseComparator (byte [] comparator) { -825 final int index = Bytes.searchDelimiterIndex(comparator, 0, comparator.length, -826 ParseConstants.COLON); -827 if (index == -1) { -828 throw new IllegalArgumentException("Incorrect comparator"); -829 } -830 -831 byte [][] result = new byte [2][0]; -832 result[0] = new byte [index]; -833 System.arraycopy(comparator, 0, result[0], 0, index); +035import org.apache.hadoop.hbase.CompareOperator; +036import org.apache.hadoop.hbase.classification.InterfaceAudience; +037import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; +038import org.apache.hadoop.hbase.util.Bytes; +039 +040/** +041 * This class allows a user to specify a filter via a string +042 * The string is parsed using the methods of this class and +043 * a filter object is constructed. This filter object is then wrapped +044 * in a scanner object which is then returned +045 * <p> +046 * This class addresses the HBASE-4168 JIRA. More documentation on this +047 * Filter Language can be found at: https://issues.apache.org/jira/browse/HBASE-4176 +048 */ +049@InterfaceAudience.Public +050public class ParseFilter { +051 private static final Log LOG = LogFactory.getLog(ParseFilter.class); +052 +053 private static HashMap<ByteBuffer, Integer> operatorPrecedenceHashMap; +054 private static HashMap<String, String> filterHashMap; +055 +056 static { +057 // Registers all the filter supported by the Filter Language +058 filterHashMap = new HashMap<>(); +059 filterHashMap.put("KeyOnlyFilter", ParseConstants.FILTER_PACKAGE + "." + +060 "KeyOnlyFilter"); +061 filterHashMap.put("FirstKeyOnlyFilter", ParseConstants.FILTER_PACKAGE + "." + +062 "FirstKeyOnlyFilter"); +063 filterHashMap.put("PrefixFilter", ParseConstants.FILTER_PACKAGE + "." + +064 "PrefixFilter"); +065 filterHashMap.put("ColumnPrefixFilter", ParseConstants.FILTER_PACKAGE + "." + +066 "ColumnPrefixFilter"); +067 filterHashMap.put("MultipleColumnPrefixFilter", ParseConstants.FILTER_PACKAGE + "." + +068 "MultipleColumnPrefixFilter"); +069 filterHashMap.put("ColumnCountGetFilter", ParseConstants.FILTER_PACKAGE + "." + +070 "ColumnCountGetFilter"); +071 filterHashMap.put("PageFilter", ParseConstants.FILTER_PACKAGE + "." + +072 "PageFilter"); +073 filterHashMap.put("ColumnPaginationFilter", ParseConstants.FILTER_PACKAGE + "." + +074 "ColumnPaginationFilter"); +075 filterHashMap.put("InclusiveStopFilter", ParseConstants.FILTER_PACKAGE + "." + +076 "InclusiveStopFilter"); +077 filterHashMap.put("TimestampsFilter", ParseConstants.FILTER_PACKAGE + "." + +078 "TimestampsFilter"); +079 filterHashMap.put("RowFilter", ParseConstants.FILTER_PACKAGE + "." + +080 "RowFilter"); +081 filterHashMap.put("FamilyFilter", ParseConstants.FILTER_PACKAGE + "." + +082 "FamilyFilter"); +083 filterHashMap.put("QualifierFilter", ParseConstants.FILTER_PACKAGE + "." + +084 "QualifierFilter"); +085 filterHashMap.put("ValueFilter", ParseConstants.FILTER_PACKAGE + "." + +086 "ValueFilter"); +087 filterHashMap.put("ColumnRangeFilter", ParseConstants.FILTER_PACKAGE + "." + +088 "ColumnRangeFilter"); +089 filterHashMap.put("SingleColumnValueFilter", ParseConstants.FILTER_PACKAGE + "." + +090 "SingleColumnValueFilter"); +