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 boolean execute(Record record) { 150 FieldValue fieldValue = record.get(field); 151 if (fieldValue == null) { 152 return false; 153 } 154 155 if (operator == Operator.EQUAL) { 156 boolean ret; 157 if (ignoreCase) { 158 ret = fieldValue.asString().toLowerCase().contains(value.asString().toLowerCase()); 159 } else { 160 ret = fieldValue.asString().contains(value.asString()); 161 } 162 return not != ret; 163 } 164 165 int compare = ignoreCase ? 166 fieldValue.compareToIgnoreCase(value) : fieldValue.compareTo(value); 167 168 boolean ret; 169 switch (operator) { 170 case DOUBLE_EQUALS: 171 ret = compare == 0; 172 break; 173 174 case GREATER: 175 ret = compare > 0; 176 break; 177 178 case GREATER_OR_EQUAL: 179 ret = compare >= 0; 180 break; 181 182 case LESS: 183 ret = compare < 0; 184 break; 185 186 case LESS_OR_EQUAL: 187 ret = compare <= 0; 188 break; 189 190 default: 191 throw new AssertionError(); 192 } 193 return not != ret; 194 } 195 196 @Override 197 public String toString() { 198 return (not ? "!" : "") + field.getHeader() + operator + value.asString(); 199 } 200 201 @Override 202 public boolean equals(Object o) { 203 if (this == o) { 204 return true; 205 } 206 if (!(o instanceof RecordFilter)) { 207 return false; 208 } 209 RecordFilter filter = (RecordFilter) o; 210 return ignoreCase == filter.ignoreCase && not == filter.not && field == filter.field 211 && operator == filter.operator && value.equals(filter.value); 212 } 213 214 @Override 215 public int hashCode() { 216 return Objects.hash(ignoreCase, not, field, operator, value); 217 } 218 219 /* 220 * For FilterBuilder 221 */ 222 public static FilterBuilder newBuilder(Field field) { 223 return new FilterBuilder(field, false); 224 } 225 226 public static FilterBuilder newBuilder(Field field, boolean ignoreCase) { 227 return new FilterBuilder(field, ignoreCase); 228 } 229 230 public static final class FilterBuilder { 231 private final Field field; 232 private final boolean ignoreCase; 233 234 private FilterBuilder(Field field, boolean ignoreCase) { 235 this.field = Objects.requireNonNull(field); 236 this.ignoreCase = ignoreCase; 237 } 238 239 public RecordFilter equal(FieldValue value) { 240 return newFilter(false, Operator.EQUAL, value); 241 } 242 243 public RecordFilter equal(Object value) { 244 return equal(field.newValue(value)); 245 } 246 247 public RecordFilter notEqual(FieldValue value) { 248 return newFilter(true, Operator.EQUAL, value); 249 } 250 251 public RecordFilter notEqual(Object value) { 252 return notEqual(field.newValue(value)); 253 } 254 255 public RecordFilter doubleEquals(FieldValue value) { 256 return newFilter(false, Operator.DOUBLE_EQUALS, value); 257 } 258 259 public RecordFilter doubleEquals(Object value) { 260 return doubleEquals(field.newValue(value)); 261 } 262 263 public RecordFilter notDoubleEquals(FieldValue value) { 264 return newFilter(true, Operator.DOUBLE_EQUALS, value); 265 } 266 267 public RecordFilter notDoubleEquals(Object value) { 268 return notDoubleEquals(field.newValue(value)); 269 } 270 271 public RecordFilter greater(FieldValue value) { 272 return newFilter(false, Operator.GREATER, value); 273 } 274 275 public RecordFilter greater(Object value) { 276 return greater(field.newValue(value)); 277 } 278 279 public RecordFilter notGreater(FieldValue value) { 280 return newFilter(true, Operator.GREATER, value); 281 } 282 283 public RecordFilter notGreater(Object value) { 284 return notGreater(field.newValue(value)); 285 } 286 287 public RecordFilter greaterOrEqual(FieldValue value) { 288 return newFilter(false, Operator.GREATER_OR_EQUAL, value); 289 } 290 291 public RecordFilter greaterOrEqual(Object value) { 292 return greaterOrEqual(field.newValue(value)); 293 } 294 295 public RecordFilter notGreaterOrEqual(FieldValue value) { 296 return newFilter(true, Operator.GREATER_OR_EQUAL, value); 297 } 298 299 public RecordFilter notGreaterOrEqual(Object value) { 300 return notGreaterOrEqual(field.newValue(value)); 301 } 302 303 public RecordFilter less(FieldValue value) { 304 return newFilter(false, Operator.LESS, value); 305 } 306 307 public RecordFilter less(Object value) { 308 return less(field.newValue(value)); 309 } 310 311 public RecordFilter notLess(FieldValue value) { 312 return newFilter(true, Operator.LESS, value); 313 } 314 315 public RecordFilter notLess(Object value) { 316 return notLess(field.newValue(value)); 317 } 318 319 public RecordFilter lessOrEqual(FieldValue value) { 320 return newFilter(false, Operator.LESS_OR_EQUAL, value); 321 } 322 323 public RecordFilter lessOrEqual(Object value) { 324 return lessOrEqual(field.newValue(value)); 325 } 326 327 public RecordFilter notLessOrEqual(FieldValue value) { 328 return newFilter(true, Operator.LESS_OR_EQUAL, value); 329 } 330 331 public RecordFilter notLessOrEqual(Object value) { 332 return notLessOrEqual(field.newValue(value)); 333 } 334 335 private RecordFilter newFilter(boolean not, Operator operator, FieldValue value) { 336 return new RecordFilter(ignoreCase, not, field, operator, value); 337 } 338 } 339}