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.field;
019
020import edu.umd.cs.findbugs.annotations.NonNull;
021import java.util.Objects;
022import org.apache.hadoop.hbase.Size;
023import org.apache.yetus.audience.InterfaceAudience;
024
025/**
026 * Represents a value of a field. The type of a value is defined by {@link FieldValue}.
027 */
028@InterfaceAudience.Private
029public final class FieldValue implements Comparable<FieldValue> {
030
031  private final Object value;
032  private final FieldValueType type;
033
034  FieldValue(Object value, FieldValueType type) {
035    Objects.requireNonNull(value);
036    this.type = Objects.requireNonNull(type);
037
038    switch (type) {
039      case STRING:
040        if (value instanceof String) {
041          this.value = value;
042          break;
043        }
044        throw new IllegalArgumentException("invalid type");
045
046      case INTEGER:
047        if (value instanceof Integer) {
048          this.value = value;
049          break;
050        } else if (value instanceof String) {
051          this.value = Integer.valueOf((String) value);
052          break;
053        }
054        throw new IllegalArgumentException("invalid type");
055
056      case LONG:
057        if (value instanceof Long) {
058          this.value = value;
059          break;
060        } else if (value instanceof String) {
061          this.value = Long.valueOf((String) value);
062          break;
063        }
064        throw new IllegalArgumentException("invalid type");
065
066      case FLOAT:
067        if (value instanceof Float) {
068          this.value = value;
069          break;
070        } else if (value instanceof String) {
071          this.value = Float.valueOf((String) value);
072          break;
073        }
074        throw new IllegalArgumentException("invalid type");
075
076      case SIZE:
077        if (value instanceof Size) {
078          this.value = optimizeSize((Size) value);
079          break;
080        } else if (value instanceof String) {
081          this.value = optimizeSize(parseSizeString((String) value));
082          break;
083        }
084        throw new IllegalArgumentException("invalid type");
085
086      case PERCENT:
087        if (value instanceof Float) {
088          this.value = value;
089          break;
090        } else if (value instanceof String) {
091          this.value = parsePercentString((String) value);
092          break;
093        }
094        throw new IllegalArgumentException("invalid type");
095
096      default:
097        throw new AssertionError();
098    }
099  }
100
101  private Size optimizeSize(Size size) {
102    if (size.get(Size.Unit.BYTE) < 1024d) {
103      return size.getUnit() == Size.Unit.BYTE
104        ? size
105        : new Size(size.get(Size.Unit.BYTE), Size.Unit.BYTE);
106    } else if (size.get(Size.Unit.KILOBYTE) < 1024d) {
107      return size.getUnit() == Size.Unit.KILOBYTE
108        ? size
109        : new Size(size.get(Size.Unit.KILOBYTE), Size.Unit.KILOBYTE);
110    } else if (size.get(Size.Unit.MEGABYTE) < 1024d) {
111      return size.getUnit() == Size.Unit.MEGABYTE
112        ? size
113        : new Size(size.get(Size.Unit.MEGABYTE), Size.Unit.MEGABYTE);
114    } else if (size.get(Size.Unit.GIGABYTE) < 1024d) {
115      return size.getUnit() == Size.Unit.GIGABYTE
116        ? size
117        : new Size(size.get(Size.Unit.GIGABYTE), Size.Unit.GIGABYTE);
118    } else if (size.get(Size.Unit.TERABYTE) < 1024d) {
119      return size.getUnit() == Size.Unit.TERABYTE
120        ? size
121        : new Size(size.get(Size.Unit.TERABYTE), Size.Unit.TERABYTE);
122    }
123    return size.getUnit() == Size.Unit.PETABYTE
124      ? size
125      : new Size(size.get(Size.Unit.PETABYTE), Size.Unit.PETABYTE);
126  }
127
128  private Size parseSizeString(String sizeString) {
129    if (sizeString.length() < 3) {
130      throw new IllegalArgumentException("invalid size");
131    }
132
133    String valueString = sizeString.substring(0, sizeString.length() - 2);
134    String unitSimpleName = sizeString.substring(sizeString.length() - 2);
135    return new Size(Double.parseDouble(valueString), convertToUnit(unitSimpleName));
136  }
137
138  private Size.Unit convertToUnit(String unitSimpleName) {
139    for (Size.Unit unit : Size.Unit.values()) {
140      if (unitSimpleName.equals(unit.getSimpleName())) {
141        return unit;
142      }
143    }
144    throw new IllegalArgumentException("invalid size");
145  }
146
147  private Float parsePercentString(String percentString) {
148    if (percentString.endsWith("%")) {
149      percentString = percentString.substring(0, percentString.length() - 1);
150    }
151    return Float.valueOf(percentString);
152  }
153
154  public String asString() {
155    return toString();
156  }
157
158  public int asInt() {
159    return (Integer) value;
160  }
161
162  public long asLong() {
163    return (Long) value;
164  }
165
166  public float asFloat() {
167    return (Float) value;
168  }
169
170  public Size asSize() {
171    return (Size) value;
172  }
173
174  @Override
175  public String toString() {
176    switch (type) {
177      case STRING:
178      case INTEGER:
179      case LONG:
180      case FLOAT:
181        return value.toString();
182
183      case SIZE:
184        Size size = (Size) value;
185        return String.format("%.1f", size.get()) + size.getUnit().getSimpleName();
186
187      case PERCENT:
188        return String.format("%.2f", (Float) value) + "%";
189
190      default:
191        throw new AssertionError();
192    }
193  }
194
195  @Override
196  public int compareTo(@NonNull FieldValue o) {
197    if (type != o.type) {
198      throw new IllegalArgumentException("invalid type");
199    }
200
201    switch (type) {
202      case STRING:
203        return ((String) value).compareTo((String) o.value);
204
205      case INTEGER:
206        return ((Integer) value).compareTo((Integer) o.value);
207
208      case LONG:
209        return ((Long) value).compareTo((Long) o.value);
210
211      case FLOAT:
212      case PERCENT:
213        return ((Float) value).compareTo((Float) o.value);
214
215      case SIZE:
216        return ((Size) value).compareTo((Size) o.value);
217
218      default:
219        throw new AssertionError();
220    }
221  }
222
223  @Override
224  public boolean equals(Object o) {
225    if (this == o) {
226      return true;
227    }
228    if (!(o instanceof FieldValue)) {
229      return false;
230    }
231    FieldValue that = (FieldValue) o;
232    return value.equals(that.value) && type == that.type;
233  }
234
235  @Override
236  public int hashCode() {
237    return Objects.hash(value, type);
238  }
239
240  public FieldValue plus(FieldValue o) {
241    if (type != o.type) {
242      throw new IllegalArgumentException("invalid type");
243    }
244
245    switch (type) {
246      case STRING:
247        return new FieldValue(((String) value).concat((String) o.value), type);
248
249      case INTEGER:
250        return new FieldValue(((Integer) value) + ((Integer) o.value), type);
251
252      case LONG:
253        return new FieldValue(((Long) value) + ((Long) o.value), type);
254
255      case FLOAT:
256      case PERCENT:
257        return new FieldValue(((Float) value) + ((Float) o.value), type);
258
259      case SIZE:
260        Size size = (Size) value;
261        Size oSize = (Size) o.value;
262        Size.Unit unit = size.getUnit();
263        return new FieldValue(new Size(size.get(unit) + oSize.get(unit), unit), type);
264
265      default:
266        throw new AssertionError();
267    }
268  }
269
270  public int compareToIgnoreCase(FieldValue o) {
271    if (type != o.type) {
272      throw new IllegalArgumentException("invalid type");
273    }
274
275    switch (type) {
276      case STRING:
277        return ((String) value).compareToIgnoreCase((String) o.value);
278
279      case INTEGER:
280      case LONG:
281      case FLOAT:
282      case SIZE:
283      case PERCENT:
284        return compareTo(o);
285
286      default:
287        throw new AssertionError();
288    }
289  }
290}