/* * -----------------------------------------------------------------------\ * PerfCake *   * Copyright (C) 2010 - 2016 the original author or authors. *   * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * -----------------------------------------------------------------------/ */ package org.perfcake.reporting; import java.util.Locale; /** * A number with a scalable unit. * * @author <a href="mailto:pavel.macik@gmail.com">Pavel Macík</a> */ public abstract class ScalableQuantity<N extends Number> extends Quantity<N> { /** * <p>The base power for the quantity value.</p> * * <p>For example in case of decimal numbers, when we want to insert 123 ms, we don't have to re-compute the value to the base unit power * (and pass it as <code>0.123 s</code>. With the base power we can pass the number value as is <code>123</code> and pass it with * the base power of <code>-1</code>.</p> */ private int basePower = 0; /** * Creates a new scalable quantity. * * @param number * The value. * @param unit * The unit of the value. */ public ScalableQuantity(final N number, final String unit) { this(number, 0, unit); } /** * <p>Creates a new scalable quantity with the specified base power.</p> * * <p>For example in case of decimal numbers, when we want to insert 123 ms, we don't have to re-compute the value to the base unit power * (and pass it as <code>0.123 s</code>. With the base power we can pass the number value as is <code>123</code> and pass it with * the base power of <code>-1</code>.</p> * * @param number * The value. * @param basePower * The base power of the value. * @param unit * The unit of the value. */ public ScalableQuantity(final N number, final int basePower, final String unit) { super(number, unit); if (basePower < getMinPower() || basePower > getMaxPower()) { throw new IllegalArgumentException("Base power " + basePower + " is out of range. It should be between " + getMinPower() + " and " + getMaxPower() + "."); } this.basePower = basePower; } /** * <p>Gets a scale factor of the quantity.</p> * * <p>For example the decimal numbers have scale factor for prefixes with the value of 1000.</p> * * @return A scalefactor for the current quantity. **/ protected abstract N getScaleFactor(); /** * Do the actual mapping of the scaling power to the unit prefix. After the power value was bounded to the * range of {@link #getMinPower()} to {@link #getMaxPower()}. * * @param power * Power of the base quantity * @return The scale prefix for the number. **/ protected abstract String getBoundedScalePrefix(final int power); /** * Gets a minimal power for which the scalable quantity has a unit prefix. * * @return The minimal power value. **/ protected abstract int getMinPower(); /** * Gets a max power for which the scalable quantity has a unit prefix. * * @return The minimal power value. **/ protected abstract int getMaxPower(); /** * <p>Gets the base power for the quantity value.</p> * * <p>For example in case of decimal numbers, when we want to insert 123 ms, we don't have to re-compute the value to the base unit power * (and pass it as <code>0.123 s</code>. With the base power we can pass the number value as is <code>123</code> and pass it with * the base power of <code>-1</code>.</p> * * @return The base power value. */ protected int getBasePower() { return basePower; } /** * <p>Gets a unit prefix for the current value of the quantity.</p> * * <p>For example in the case of decimal numbers the scale prefix for the value of 1,000 * would be <code>k</code> or for the value of 1,000,000 would be <code>M</code> which needs * to be passed as 1 and 2 respectively. The result is the prefix for the number of 10^(power * 3).</p> * * @param power * Scale power of the base quantity I.e. 10^(power * 3) for decimal numbers. * @return The scale prefix for the number. E.g. 10^(power * 3), -2 = μ, -1 = m, 1 = k, 2 = M, 3 = G etc. for decimal numbers. **/ protected String getScalePrefix(final int power) { int boundedPower; if (power < getMinPower()) { boundedPower = getMinPower(); } else if (power > getMaxPower()) { boundedPower = getMaxPower(); } else { boundedPower = power; } return getBoundedScalePrefix(boundedPower); } @Override public String toString() { double valuePan = getNumber().doubleValue(); final double scaleFactor = getScaleFactor().doubleValue(); int i = getBasePower(); if (valuePan > 1.0) { // need to scale the value down while (valuePan >= scaleFactor && i < getMaxPower()) { valuePan = valuePan / scaleFactor; i++; } } else { while (valuePan < 1.0 && i > getMinPower()) { valuePan = valuePan * scaleFactor; i--; } } return String.format(Locale.US, "%.2f", valuePan) + " " + getScalePrefix(i) + getUnit(); } }