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}