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