001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.hbtop; 019 020import java.util.Arrays; 021import java.util.List; 022import java.util.Objects; 023import org.apache.hadoop.hbase.hbtop.field.Field; 024import org.apache.hadoop.hbase.hbtop.field.FieldValue; 025import org.apache.yetus.audience.InterfaceAudience; 026 027/** 028 * Represents a filter that's filtering the metric {@link Record}s. 029 */ 030@InterfaceAudience.Private 031public final class RecordFilter { 032 033 private enum Operator { 034 EQUAL("="), 035 DOUBLE_EQUALS("=="), 036 GREATER(">"), 037 GREATER_OR_EQUAL(">="), 038 LESS("<"), 039 LESS_OR_EQUAL("<="); 040 041 private final String operator; 042 043 Operator(String operator) { 044 this.operator = operator; 045 } 046 047 @Override 048 public String toString() { 049 return operator; 050 } 051 } 052 053 public static RecordFilter parse(String filterString, boolean ignoreCase) { 054 return parse(filterString, Arrays.asList(Field.values()), ignoreCase); 055 } 056 057 /* 058 * Parse a filter string and build a RecordFilter instance. 059 */ 060 public static RecordFilter parse(String filterString, List<Field> fields, boolean ignoreCase) { 061 int index = 0; 062 063 boolean not = isNot(filterString); 064 if (not) { 065 index += 1; 066 } 067 068 StringBuilder fieldString = new StringBuilder(); 069 while ( 070 filterString.length() > index && filterString.charAt(index) != '<' 071 && filterString.charAt(index) != '>' && filterString.charAt(index) != '=' 072 ) { 073 fieldString.append(filterString.charAt(index++)); 074 } 075 076 if (fieldString.length() == 0 || filterString.length() == index) { 077 return null; 078 } 079 080 Field field = getField(fields, fieldString.toString()); 081 if (field == null) { 082 return null; 083 } 084 085 StringBuilder operatorString = new StringBuilder(); 086 while ( 087 filterString.length() > index && (filterString.charAt(index) == '<' 088 || filterString.charAt(index) == '>' || filterString.charAt(index) == '=') 089 ) { 090 operatorString.append(filterString.charAt(index++)); 091 } 092 093 Operator operator = getOperator(operatorString.toString()); 094 if (operator == null) { 095 return null; 096 } 097 098 String value = filterString.substring(index); 099 FieldValue fieldValue = getFieldValue(field, value); 100 if (fieldValue == null) { 101 return null; 102 } 103 104 return new RecordFilter(ignoreCase, not, field, operator, fieldValue); 105 } 106 107 private static FieldValue getFieldValue(Field field, String value) { 108 try { 109 return field.newValue(value); 110 } catch (Exception e) { 111 return null; 112 } 113 } 114 115 private static boolean isNot(String filterString) { 116 return filterString.startsWith("!"); 117 } 118 119 private static Field getField(List<Field> fields, String fieldString) { 120 for (Field f : fields) { 121 if (f.getHeader().equals(fieldString)) { 122 return f; 123 } 124 } 125 return null; 126 } 127 128 private static Operator getOperator(String operatorString) { 129 for (Operator o : Operator.values()) { 130 if (operatorString.equals(o.toString())) { 131 return o; 132 } 133 } 134 return null; 135 } 136 137 private final boolean ignoreCase; 138 private final boolean not; 139 private final Field field; 140 private final Operator operator; 141 private final FieldValue value; 142 143 private RecordFilter(boolean ignoreCase, boolean not, Field field, Operator operator, 144 FieldValue value) { 145 this.ignoreCase = ignoreCase; 146 this.not = not; 147 this.field = Objects.requireNonNull(field); 148 this.operator = Objects.requireNonNull(operator); 149 this.value = Objects.requireNonNull(value); 150 } 151 152 public Field getField() { 153 return field; 154 } 155 156 public boolean execute(Record record) { 157 FieldValue fieldValue = record.get(field); 158 if (fieldValue == null) { 159 return false; 160 } 161 162 if (operator == Operator.EQUAL) { 163 boolean ret; 164 if (ignoreCase) { 165 ret = fieldValue.asString().toLowerCase().contains(value.asString().toLowerCase()); 166 } else { 167 ret = fieldValue.asString().contains(value.asString()); 168 } 169 return not != ret; 170 } 171 172 int compare = ignoreCase ? fieldValue.compareToIgnoreCase(value) : fieldValue.compareTo(value); 173 174 boolean ret; 175 switch (operator) { 176 case DOUBLE_EQUALS: 177 ret = compare == 0; 178 break; 179 180 case GREATER: 181 ret = compare > 0; 182 break; 183 184 case GREATER_OR_EQUAL: 185 ret = compare >= 0; 186 break; 187 188 case LESS: 189 ret = compare < 0; 190 break; 191 192 case LESS_OR_EQUAL: 193 ret = compare <= 0; 194 break; 195 196 default: 197 throw new AssertionError(); 198 } 199 return not != ret; 200 } 201 202 @Override 203 public String toString() { 204 return (not ? "!" : "") + field.getHeader() + operator + value.asString(); 205 } 206 207 @Override 208 public boolean equals(Object o) { 209 if (this == o) { 210 return true; 211 } 212 if (!(o instanceof RecordFilter)) { 213 return false; 214 } 215 RecordFilter filter = (RecordFilter) o; 216 return ignoreCase == filter.ignoreCase && not == filter.not && field == filter.field 217 && operator == filter.operator && value.equals(filter.value); 218 } 219 220 @Override 221 public int hashCode() { 222 return Objects.hash(ignoreCase, not, field, operator, value); 223 } 224 225 /* 226 * For FilterBuilder 227 */ 228 public static FilterBuilder newBuilder(Field field) { 229 return new FilterBuilder(field, false); 230 } 231 232 public static FilterBuilder newBuilder(Field field, boolean ignoreCase) { 233 return new FilterBuilder(field, ignoreCase); 234 } 235 236 public static final class FilterBuilder { 237 private final Field field; 238 private final boolean ignoreCase; 239 240 private FilterBuilder(Field field, boolean ignoreCase) { 241 this.field = Objects.requireNonNull(field); 242 this.ignoreCase = ignoreCase; 243 } 244 245 public RecordFilter equal(FieldValue value) { 246 return newFilter(false, Operator.EQUAL, value); 247 } 248 249 public RecordFilter equal(Object value) { 250 return equal(field.newValue(value)); 251 } 252 253 public RecordFilter notEqual(FieldValue value) { 254 return newFilter(true, Operator.EQUAL, value); 255 } 256 257 public RecordFilter notEqual(Object value) { 258 return notEqual(field.newValue(value)); 259 } 260 261 public RecordFilter doubleEquals(FieldValue value) { 262 return newFilter(false, Operator.DOUBLE_EQUALS, value); 263 } 264 265 public RecordFilter doubleEquals(Object value) { 266 return doubleEquals(field.newValue(value)); 267 } 268 269 public RecordFilter notDoubleEquals(FieldValue value) { 270 return newFilter(true, Operator.DOUBLE_EQUALS, value); 271 } 272 273 public RecordFilter notDoubleEquals(Object value) { 274 return notDoubleEquals(field.newValue(value)); 275 } 276 277 public RecordFilter greater(FieldValue value) { 278 return newFilter(false, Operator.GREATER, value); 279 } 280 281 public RecordFilter greater(Object value) { 282 return greater(field.newValue(value)); 283 } 284 285 public RecordFilter notGreater(FieldValue value) { 286 return newFilter(true, Operator.GREATER, value); 287 } 288 289 public RecordFilter notGreater(Object value) { 290 return notGreater(field.newValue(value)); 291 } 292 293 public RecordFilter greaterOrEqual(FieldValue value) { 294 return newFilter(false, Operator.GREATER_OR_EQUAL, value); 295 } 296 297 public RecordFilter greaterOrEqual(Object value) { 298 return greaterOrEqual(field.newValue(value)); 299 } 300 301 public RecordFilter notGreaterOrEqual(FieldValue value) { 302 return newFilter(true, Operator.GREATER_OR_EQUAL, value); 303 } 304 305 public RecordFilter notGreaterOrEqual(Object value) { 306 return notGreaterOrEqual(field.newValue(value)); 307 } 308 309 public RecordFilter less(FieldValue value) { 310 return newFilter(false, Operator.LESS, value); 311 } 312 313 public RecordFilter less(Object value) { 314 return less(field.newValue(value)); 315 } 316 317 public RecordFilter notLess(FieldValue value) { 318 return newFilter(true, Operator.LESS, value); 319 } 320 321 public RecordFilter notLess(Object value) { 322 return notLess(field.newValue(value)); 323 } 324 325 public RecordFilter lessOrEqual(FieldValue value) { 326 return newFilter(false, Operator.LESS_OR_EQUAL, value); 327 } 328 329 public RecordFilter lessOrEqual(Object value) { 330 return lessOrEqual(field.newValue(value)); 331 } 332 333 public RecordFilter notLessOrEqual(FieldValue value) { 334 return newFilter(true, Operator.LESS_OR_EQUAL, value); 335 } 336 337 public RecordFilter notLessOrEqual(Object value) { 338 return notLessOrEqual(field.newValue(value)); 339 } 340 341 private RecordFilter newFilter(boolean not, Operator operator, FieldValue value) { 342 return new RecordFilter(ignoreCase, not, field, operator, value); 343 } 344 } 345}