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/** 029 * Represents a filter that's filtering the metric {@link Record}s. 030 */ 031@InterfaceAudience.Private 032public final class RecordFilter { 033 034 private enum Operator { 035 EQUAL("="), 036 DOUBLE_EQUALS("=="), 037 GREATER(">"), 038 GREATER_OR_EQUAL(">="), 039 LESS("<"), 040 LESS_OR_EQUAL("<="); 041 042 private final String operator; 043 044 Operator(String operator) { 045 this.operator = operator; 046 } 047 048 @Override 049 public String toString() { 050 return operator; 051 } 052 } 053 054 public static RecordFilter parse(String filterString, boolean ignoreCase) { 055 return parse(filterString, Arrays.asList(Field.values()), ignoreCase); 056 } 057 058 /* 059 * Parse a filter string and build a RecordFilter instance. 060 */ 061 public static RecordFilter parse(String filterString, List<Field> fields, boolean ignoreCase) { 062 int index = 0; 063 064 boolean not = isNot(filterString); 065 if (not) { 066 index += 1; 067 } 068 069 StringBuilder fieldString = new StringBuilder(); 070 while (filterString.length() > index && filterString.charAt(index) != '<' 071 && filterString.charAt(index) != '>' && filterString.charAt(index) != '=') { 072 fieldString.append(filterString.charAt(index++)); 073 } 074 075 if (fieldString.length() == 0 || filterString.length() == index) { 076 return null; 077 } 078 079 Field field = getField(fields, fieldString.toString()); 080 if (field == null) { 081 return null; 082 } 083 084 StringBuilder operatorString = new StringBuilder(); 085 while (filterString.length() > index && (filterString.charAt(index) == '<' || 086 filterString.charAt(index) == '>' || filterString.charAt(index) == '=')) { 087 operatorString.append(filterString.charAt(index++)); 088 } 089 090 Operator operator = getOperator(operatorString.toString()); 091 if (operator == null) { 092 return null; 093 } 094 095 String value = filterString.substring(index); 096 FieldValue fieldValue = getFieldValue(field, value); 097 if (fieldValue == null) { 098 return null; 099 } 100 101 return new RecordFilter(ignoreCase, not, field, operator, fieldValue); 102 } 103 104 private static FieldValue getFieldValue(Field field, String value) { 105 try { 106 return field.newValue(value); 107 } catch (Exception e) { 108 return null; 109 } 110 } 111 112 private static boolean isNot(String filterString) { 113 return filterString.startsWith("!"); 114 } 115 116 private static Field getField(List<Field> fields, String fieldString) { 117 for (Field f : fields) { 118 if (f.getHeader().equals(fieldString)) { 119 return f; 120 } 121 } 122 return null; 123 } 124 125 private static Operator getOperator(String operatorString) { 126 for (Operator o : Operator.values()) { 127 if (operatorString.equals(o.toString())) { 128 return o; 129 } 130 } 131 return null; 132 } 133 134 private final boolean ignoreCase; 135 private final boolean not; 136 private final Field field; 137 private final Operator operator; 138 private final FieldValue value; 139 140 private RecordFilter(boolean ignoreCase, boolean not, Field field, Operator operator, 141 FieldValue value) { 142 this.ignoreCase = ignoreCase; 143 this.not = not; 144 this.field = Objects.requireNonNull(field); 145 this.operator = Objects.requireNonNull(operator); 146 this.value = Objects.requireNonNull(value); 147 } 148 149 public Field getField() { 150 return field; 151 } 152 153 public boolean execute(Record record) { 154 FieldValue fieldValue = record.get(field); 155 if (fieldValue == null) { 156 return false; 157 } 158 159 if (operator == Operator.EQUAL) { 160 boolean ret; 161 if (ignoreCase) { 162 ret = fieldValue.asString().toLowerCase().contains(value.asString().toLowerCase()); 163 } else { 164 ret = fieldValue.asString().contains(value.asString()); 165 } 166 return not != ret; 167 } 168 169 int compare = ignoreCase ? 170 fieldValue.compareToIgnoreCase(value) : fieldValue.compareTo(value); 171 172 boolean ret; 173 switch (operator) { 174 case DOUBLE_EQUALS: 175 ret = compare == 0; 176 break; 177 178 case GREATER: 179 ret = compare > 0; 180 break; 181 182 case GREATER_OR_EQUAL: 183 ret = compare >= 0; 184 break; 185 186 case LESS: 187 ret = compare < 0; 188 break; 189 190 case LESS_OR_EQUAL: 191 ret = compare <= 0; 192 break; 193 194 default: 195 throw new AssertionError(); 196 } 197 return not != ret; 198 } 199 200 @Override 201 public String toString() { 202 return (not ? "!" : "") + field.getHeader() + operator + value.asString(); 203 } 204 205 @Override 206 public boolean equals(Object o) { 207 if (this == o) { 208 return true; 209 } 210 if (!(o instanceof RecordFilter)) { 211 return false; 212 } 213 RecordFilter filter = (RecordFilter) o; 214 return ignoreCase == filter.ignoreCase && not == filter.not && field == filter.field 215 && operator == filter.operator && value.equals(filter.value); 216 } 217 218 @Override 219 public int hashCode() { 220 return Objects.hash(ignoreCase, not, field, operator, value); 221 } 222 223 /* 224 * For FilterBuilder 225 */ 226 public static FilterBuilder newBuilder(Field field) { 227 return new FilterBuilder(field, false); 228 } 229 230 public static FilterBuilder newBuilder(Field field, boolean ignoreCase) { 231 return new FilterBuilder(field, ignoreCase); 232 } 233 234 public static final class FilterBuilder { 235 private final Field field; 236 private final boolean ignoreCase; 237 238 private FilterBuilder(Field field, boolean ignoreCase) { 239 this.field = Objects.requireNonNull(field); 240 this.ignoreCase = ignoreCase; 241 } 242 243 public RecordFilter equal(FieldValue value) { 244 return newFilter(false, Operator.EQUAL, value); 245 } 246 247 public RecordFilter equal(Object value) { 248 return equal(field.newValue(value)); 249 } 250 251 public RecordFilter notEqual(FieldValue value) { 252 return newFilter(true, Operator.EQUAL, value); 253 } 254 255 public RecordFilter notEqual(Object value) { 256 return notEqual(field.newValue(value)); 257 } 258 259 public RecordFilter doubleEquals(FieldValue value) { 260 return newFilter(false, Operator.DOUBLE_EQUALS, value); 261 } 262 263 public RecordFilter doubleEquals(Object value) { 264 return doubleEquals(field.newValue(value)); 265 } 266 267 public RecordFilter notDoubleEquals(FieldValue value) { 268 return newFilter(true, Operator.DOUBLE_EQUALS, value); 269 } 270 271 public RecordFilter notDoubleEquals(Object value) { 272 return notDoubleEquals(field.newValue(value)); 273 } 274 275 public RecordFilter greater(FieldValue value) { 276 return newFilter(false, Operator.GREATER, value); 277 } 278 279 public RecordFilter greater(Object value) { 280 return greater(field.newValue(value)); 281 } 282 283 public RecordFilter notGreater(FieldValue value) { 284 return newFilter(true, Operator.GREATER, value); 285 } 286 287 public RecordFilter notGreater(Object value) { 288 return notGreater(field.newValue(value)); 289 } 290 291 public RecordFilter greaterOrEqual(FieldValue value) { 292 return newFilter(false, Operator.GREATER_OR_EQUAL, value); 293 } 294 295 public RecordFilter greaterOrEqual(Object value) { 296 return greaterOrEqual(field.newValue(value)); 297 } 298 299 public RecordFilter notGreaterOrEqual(FieldValue value) { 300 return newFilter(true, Operator.GREATER_OR_EQUAL, value); 301 } 302 303 public RecordFilter notGreaterOrEqual(Object value) { 304 return notGreaterOrEqual(field.newValue(value)); 305 } 306 307 public RecordFilter less(FieldValue value) { 308 return newFilter(false, Operator.LESS, value); 309 } 310 311 public RecordFilter less(Object value) { 312 return less(field.newValue(value)); 313 } 314 315 public RecordFilter notLess(FieldValue value) { 316 return newFilter(true, Operator.LESS, value); 317 } 318 319 public RecordFilter notLess(Object value) { 320 return notLess(field.newValue(value)); 321 } 322 323 public RecordFilter lessOrEqual(FieldValue value) { 324 return newFilter(false, Operator.LESS_OR_EQUAL, value); 325 } 326 327 public RecordFilter lessOrEqual(Object value) { 328 return lessOrEqual(field.newValue(value)); 329 } 330 331 public RecordFilter notLessOrEqual(FieldValue value) { 332 return newFilter(true, Operator.LESS_OR_EQUAL, value); 333 } 334 335 public RecordFilter notLessOrEqual(Object value) { 336 return notLessOrEqual(field.newValue(value)); 337 } 338 339 private RecordFilter newFilter(boolean not, Operator operator, FieldValue value) { 340 return new RecordFilter(ignoreCase, not, field, operator, value); 341 } 342 } 343}