/*
*
* * RHQ Management Platform
* * Copyright (C) 2005-2012 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 as published by
* * the Free Software Foundation version 2 of the License.
* *
* * 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 for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program; if not, write to the Free Software
* * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
package org.rhq.enterprise.server.rest.reporting;
import java.text.DecimalFormat;
import org.rhq.core.domain.measurement.MeasurementUnits;
import org.rhq.core.domain.measurement.composite.MeasurementNumericValueAndUnits;
import org.rhq.core.domain.measurement.util.MeasurementConversionException;
/**
* @author jsanda
*/
public class MeasurementConverter {
private static final int MAX_PRECISION_DIGITS = 4;
private static final String NULL_OR_NAN_FORMATTED_VALUE = "--no data available--";
public static String format(Double value, MeasurementUnits targetUnits, boolean bestFit) {
return format(value, targetUnits, bestFit, null, null);
}
public static String format(Double value, MeasurementUnits targetUnits, boolean bestFit,
Integer minimumFractionDigits, Integer maximumFractionDigits) {
if (value == null || Double.isNaN(value)) {
return NULL_OR_NAN_FORMATTED_VALUE;
}
if (bestFit) {
MeasurementNumericValueAndUnits valueAndUnits = fit(value, targetUnits);
value = valueAndUnits.getValue();
targetUnits = valueAndUnits.getUnits();
}
// apply relative scale at presentation time
if (targetUnits != null && MeasurementUnits.Family.RELATIVE == targetUnits.getFamily()) {
value = MeasurementUnits.scaleUp(value, targetUnits);
}
DecimalFormat numberFormat = new DecimalFormat(getFormat(
minimumFractionDigits != null ? minimumFractionDigits : 1,
maximumFractionDigits != null ? maximumFractionDigits : 1));
String formatted = numberFormat.format(value);
return format(formatted, targetUnits);
}
public static String format(String value, MeasurementUnits targetUnits) {
if (targetUnits == null) {
return value;
} else {
return value + getMeasurementUnitAbbreviation(targetUnits);
}
}
public static MeasurementNumericValueAndUnits fit(Double origin, MeasurementUnits units) {
return fit(origin, units, null, null);
}
public static MeasurementNumericValueAndUnits fit(Double origin, MeasurementUnits units, MeasurementUnits lowUnits,
MeasurementUnits highUnits) {
// work-around for the various Chart descendants not properly setting their units field;
if (null == units) {
return new MeasurementNumericValueAndUnits(origin, units);
}
// by definition, absolutely specified units don't scale to anything
if ((MeasurementUnits.Family.ABSOLUTE == units.getFamily())
|| (MeasurementUnits.Family.DURATION == units.getFamily())) {
return new MeasurementNumericValueAndUnits(origin, units);
}
// by definition relative-valued units are self-scaled (converted at formatting)
if (MeasurementUnits.Family.RELATIVE == units.getFamily()) {
return new MeasurementNumericValueAndUnits(origin, units);
}
if (MeasurementUnits.Family.TEMPERATURE == units.getFamily()) {
return new MeasurementNumericValueAndUnits(origin, units);
}
// if the magnitude is zero, the best-fit also will spin around forever since it won't change
if (Math.abs(origin) < 1e-9) {
return new MeasurementNumericValueAndUnits(origin, units);
}
boolean wasNegative = false;
if (origin < 0) {
wasNegative = true;
origin = -origin;
}
MeasurementNumericValueAndUnits currentValueAndUnits;
MeasurementNumericValueAndUnits nextValueAndUnits = new MeasurementNumericValueAndUnits(origin, units);
// first, make the value smaller if it's too big
int maxOrdinal = (highUnits != null) ? (highUnits.ordinal() + 1) : MeasurementUnits.values().length;
do {
currentValueAndUnits = nextValueAndUnits;
int nextOrdinal = currentValueAndUnits.getUnits().ordinal() + 1;
if (nextOrdinal == maxOrdinal) {
// we could theoretically get bigger, but we don't have any units to represent that
break;
}
MeasurementUnits biggerUnits = MeasurementUnits.values()[nextOrdinal];
if (biggerUnits.getFamily() != currentValueAndUnits.getUnits().getFamily()) {
// we're as big as we can get, break out of the loop so we can return
break;
}
Double smallerValue = scale(currentValueAndUnits, biggerUnits);
nextValueAndUnits = new MeasurementNumericValueAndUnits(smallerValue, biggerUnits);
} while (nextValueAndUnits.getValue() > 1.0);
// next, make the value bigger if it's too small
int minOrdinal = (lowUnits != null) ? (lowUnits.ordinal() - 1) : -1;
while (currentValueAndUnits.getValue() < 1.0) {
int nextOrdinal = currentValueAndUnits.getUnits().ordinal() - 1;
if (nextOrdinal == minOrdinal) {
// we could theoretically get smaller, but we don't have any units to represent that
break;
}
MeasurementUnits smallerUnits = MeasurementUnits.values()[nextOrdinal];
if (smallerUnits.getFamily() != currentValueAndUnits.getUnits().getFamily()) {
// we're as small as we can get, break out of the loop so we can return
break;
}
Double biggerValue = scale(currentValueAndUnits, smallerUnits);
nextValueAndUnits = new MeasurementNumericValueAndUnits(biggerValue, smallerUnits);
currentValueAndUnits = nextValueAndUnits;
}
if (wasNegative) {
return new MeasurementNumericValueAndUnits(-currentValueAndUnits.getValue(),
currentValueAndUnits.getUnits());
}
return currentValueAndUnits;
}
public static Double scale(MeasurementNumericValueAndUnits origin, MeasurementUnits targetUnits)
throws MeasurementConversionException {
MeasurementUnits originUnits = origin.getUnits();
Double originValue = origin.getValue();
return originValue * MeasurementUnits.calculateOffset(originUnits, targetUnits);
}
public static String getMeasurementUnitAbbreviation(MeasurementUnits units) {
switch (units) {
case NONE:
return "";
case PERCENTAGE:
return "%";
case BYTES:
return "B";
case KILOBYTES:
return "KB";
case MEGABYTES:
return "MB";
case GIGABYTES:
return "GB";
case TERABYTES:
return "TB";
case PETABYTES:
return "PB";
case BITS:
return "b";
case KILOBITS:
return "kb";
case MEGABITS:
return "Mb";
case GIGABITS:
return "Gb";
case TERABITS:
return "Tb";
case PETABITS:
return "Pb";
case EPOCH_MILLISECONDS:
return ""; // absolute time - no display
case EPOCH_SECONDS:
return ""; // absolute time - no display
case JIFFYS:
return "j";
case NANOSECONDS:
return "ns";
case MICROSECONDS:
return "us";
case MILLISECONDS:
return "ms";
case SECONDS:
return "s";
case MINUTES:
return "m";
case HOURS:
return "h";
case DAYS:
return "d";
case CELSIUS:
return "C";
case KELVIN:
return "K";
case FAHRENHEIGHT:
return "F";
default:
return units.toString();
}
}
public static String getFormat(int minDigits, int maxDigits) {
StringBuilder buf = new StringBuilder("0.");
for (int i = 0; i < minDigits; i++) {
buf.append("0");
}
for (int i = 0; i < (maxDigits - minDigits); i++) {
buf.append("#");
}
return buf.toString();
}
}