/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.domain.measurement; import java.util.ArrayList; import java.util.List; import org.rhq.core.domain.measurement.util.MeasurementConversionException; /** * Metric data values can be in one of the following known units of measurement. These enum values should correspond to * the "units" attribute enumerated type values as defined in the plugin descriptor's <metric> element. * * @author Joseph Marques */ public enum MeasurementUnits { /* * IF YOU MODIFY THIS LIST, MAKE SURE YOU CHANGE THE MeasurementConverterClient UTILITY * SO IT CAN i18n THE UNITS FOR THE GWT CLIENT!!! */ // Simple Metric Types - Absolute and Relative NONE("", Family.ABSOLUTE, Scale.ONE), // PERCENTAGE("%", Family.RELATIVE, Scale.HECTO), // // Absolute Sizes in Bytes (utilization) BYTES("B", Family.BYTES, Scale.ONE), // KILOBYTES("KB", Family.BYTES, Scale.KILO), // MEGABYTES("MB", Family.BYTES, Scale.MEGA), // GIGABYTES("GB", Family.BYTES, Scale.GIGA), // TERABYTES("TB", Family.BYTES, Scale.TERA), // PETABYTES("PB", Family.BYTES, Scale.PETA), // // Absolute Sizes in Bits (throughput) BITS("b", Family.BITS, Scale.ONE), // KILOBITS("Kb", Family.BITS, Scale.KILO), // MEGABITS("Mb", Family.BITS, Scale.MEGA), // GIGABITS("Gb", Family.BITS, Scale.GIGA), // TERABITS("Tb", Family.BITS, Scale.TERA), // PETABITS("Pb", Family.BITS, Scale.PETA), // // Absolute Time - no display, only hints to the UI how to display EPOCH_MILLISECONDS("", Family.DURATION, Scale.MILLI), // EPOCH_SECONDS("", Family.DURATION, Scale.SEC), // // Relative Time JIFFYS("j", Family.TIME, Scale.JIFFY), // NANOSECONDS("ns", Family.TIME, Scale.NANO), // MICROSECONDS("us", Family.TIME, Scale.MICRO), // MILLISECONDS("ms", Family.TIME, Scale.MILLI), // SECONDS("s", Family.TIME, Scale.SEC), // MINUTES("m", Family.TIME, Scale.MIN), // HOURS("h", Family.TIME, Scale.HOUR), // DAYS("d", Family.TIME, Scale.DAY), // // Temperature CELSIUS("C", Family.TEMPERATURE, Scale.ONE), // KELVIN("K", Family.TEMPERATURE, Scale.ONE), // FAHRENHEIGHT("F", Family.TEMPERATURE, Scale.ONE); private String displayUnits; private Family family; private Scale scale; private MeasurementUnits(String displayUnits, Family family, Scale scale) { if (displayUnits.length() > 5) { throw new RuntimeException("Screen real estate is expensive; displayUnits must be 5 characters or less"); } this.displayUnits = displayUnits; this.family = family; this.scale = scale; } public static MeasurementUnits getUsingDisplayUnits(String displayUnits, MeasurementUnits.Family family) { for (MeasurementUnits units : values()) { if ((units.getFamily() == family) && units.toString().equalsIgnoreCase(displayUnits)) { return units; } } return null; } public MeasurementUnits getBaseUnits() { if (family == Family.BYTES) { return BYTES; } else if (family == Family.BITS) { return BITS; } else if (family == Family.TIME) { return SECONDS; } else if (family == Family.TEMPERATURE) { return CELSIUS; } else if ((family == Family.ABSOLUTE) || (family == Family.DURATION) || (family == Family.RELATIVE)) { /* * Members of these families are their own base units */ return this; } return null; } public List<MeasurementUnits> getFamilyUnits() { List<MeasurementUnits> returnList = new ArrayList<MeasurementUnits>(); for (MeasurementUnits units : MeasurementUnits.values()) { if (units.family == family) { returnList.add(units); } } return returnList; } public boolean isComparableTo(MeasurementUnits other) { return family == other.family; } /** * @return <value> scaled appropriately for the provided units. non-RELATIVE simply return <value>. */ public static Double scaleUp(Double value, MeasurementUnits units) { if ((null == units) || (Family.RELATIVE != units.family)) { return value; } return Scale.scaleUp(value, units.scale); } /** * @return <value> scaled appropriately for the provided units. non-RELATIVE simply return <value>. */ public static Double scaleDown(Double value, MeasurementUnits units) { if ((null == units) || (Family.RELATIVE != units.family)) { return value; } return Scale.scaleDown(value, units.scale); } public static Double calculateOffset(MeasurementUnits first, MeasurementUnits second) throws MeasurementConversionException { if (first.isComparableTo(second) == false) { throw new MeasurementConversionException("Can not convert " + first.name() + " to " + second.name()); } return Scale.calculateOffset(first.scale, second.scale); } public Family getFamily() { return family; } public Scale getScale() { return scale; } /** * A Java bean style getter to allow us to access the enum name from JSPs (e.g. ${measureUnits.name}). * * @return the enum name */ public String getName() { return name(); } @Override public String toString() { return this.displayUnits; } public enum Family { ABSOLUTE, BITS, BYTES, DURATION, RELATIVE, TIME, TEMPERATURE; } public enum Scale { // Binary based scaling factors CENTI(Type.SIZE), // ONE(Type.SIZE, CENTI, 100), // HECTO(Type.SIZE, ONE, 100), // KILO(Type.SIZE, ONE, 1024), // MEGA(Type.SIZE, KILO, 1024), // GIGA(Type.SIZE, MEGA, 1024), // TERA(Type.SIZE, GIGA, 1024), // PETA(Type.SIZE, TERA, 1024), // // Time based scaling factors JIFFY(Type.TIME), // NANO(Type.TIME, JIFFY, 1000), // MICRO(Type.TIME, NANO, 1000), // MILLI(Type.TIME, MICRO, 1000), // SEC(Type.TIME, MILLI, 1000), // MIN(Type.TIME, SEC, 60), // HOUR(Type.TIME, MIN, 60), // DAY(Type.TIME, HOUR, 24), // WEEK(Type.TIME, DAY, 7), // YEAR(Type.TIME, WEEK, 52); enum Type { NONE, SIZE, TIME; } private Type type; private Scale comparisonScale; private double offset; private Scale(Type type) { this.type = type; } private Scale(Type type, Scale comparisonScale, double offset) { this(type); this.comparisonScale = comparisonScale; this.offset = offset; } // don't expose this, force callers through MeasurementUnits.applyScale() /** * @param value * @param scale * * @return value scaled by the supplied Scale offset. 0.0 if scale is null. */ static Double scaleUp(Double value, Scale scale) { if (null == scale) { return 0.0; } return value * scale.offset; } // don't expose this, force callers through MeasurementUnits.applyScale() /** * @param value * @param scale * * @return value scaled by the supplied Scale offset. 0.0 if scale is null. */ static Double scaleDown(Double value, Scale scale) { if (null == scale) { return 0.0; } return value / scale.offset; } // don't expose this, force callers through MeasurementUnits.getScaleOffset() static Double calculateOffset(Scale first, Scale second) { if (first.type != second.type) { /* * special return value meaning invalid comparison, though it was designed that this method would only * be calling by the outer enumeration, which wouldn't do the necessary checks in advance. so this code * path will rarely, if ever, be followed. */ return null; } if ((first.comparisonScale == null) && (second.comparisonScale == null)) { return 1.0; } Scale higher; Scale lower; if (first.comparisonScale == null) { higher = second; lower = first; } else if (second.comparisonScale == null) { higher = first; lower = second; } else { if (first.comparisonScale.ordinal() > second.comparisonScale.ordinal()) { higher = first; lower = second; } else if (first.comparisonScale.ordinal() < second.comparisonScale.ordinal()) { higher = second; lower = first; } else { return 1.0; } } double results = 1.0; Scale movingScale = higher; while ((movingScale != lower) && (movingScale.comparisonScale != null)) { results *= movingScale.offset; movingScale = movingScale.comparisonScale; } if (first == lower) { return 1 / results; // return inverse } return results; } } }