/*
* NumberFormatter.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.util;
import java.io.Serializable;
import java.text.DecimalFormat;
/**
* The world's most intelligent number formatter with the following features :-)
* <P>
* It guarantee's the display of a user-specified number of significant figures, sf <BR>
* It displays decimal format for numbers with absolute values between 1 and 10^(sf-1) <BR>
* It displays scientific notation for all other numbers (i.e. really big and really small absolute values) <BR>
* <b>note</b>: Its display integers for doubles with integer value <BR>
*
* @version $Id: NumberFormatter.java,v 1.6 2005/05/24 20:26:01 rambaut Exp $
*
* @author Alexei Drummond
*/
public class NumberFormatter implements Serializable {
private int sf;
private double upperCutoff;
private double[] cutoffTable;
private final DecimalFormat decimalFormat = new DecimalFormat();
private DecimalFormat scientificFormat = null;
private boolean isPadding = false;
private int fieldWidth;
public NumberFormatter(int sf) {
setSignificantFigures(sf);
}
public NumberFormatter(int sf, int fieldWidth) {
setSignificantFigures(sf);
setPadding(true);
setFieldWidth(fieldWidth);
}
public void setSignificantFigures(int sf) {
this.sf = sf;
upperCutoff = Math.pow(10, Math.max(sf-1, 0));
cutoffTable = new double[sf];
long num = 10;
for (int i =0; i < cutoffTable.length; i++) {
cutoffTable[i] = (double)num;
num *= 10;
}
decimalFormat.setMinimumIntegerDigits(1);
decimalFormat.setMaximumFractionDigits(Math.max(sf-1, 0));
decimalFormat.setMinimumFractionDigits(Math.max(sf-1, 0));
decimalFormat.setGroupingUsed(false);
scientificFormat = new DecimalFormat(getScientificPattern(sf));
fieldWidth = sf;
}
public void setPadding(boolean padding) {
isPadding = padding;
}
public void setFieldWidth(int fw) {
if (fw < sf+4) throw new IllegalArgumentException();
fieldWidth = fw;
}
public int getFieldWidth() { return fieldWidth; }
public String formatToFieldWidth(String s, int fieldWidth) {
int size = fieldWidth - s.length();
StringBuffer buffer = new StringBuffer(s);
for (int i =0; i < size; i++) {
buffer.append(' ');
}
return buffer.toString();
}
/**
* @return the given value formatted to have exactly then number of
* fraction digits specified.
*/
public String formatDecimal(double value, int numFractionDigits) {
decimalFormat.setMaximumFractionDigits(numFractionDigits);
decimalFormat.setMinimumFractionDigits(Math.min(numFractionDigits, 1));
return decimalFormat.format(value);
}
/**
* This method formats a number 'nicely': <BR>
* It guarantee's the display of a user-specified total significant figures, sf <BR>
* It displays decimal format for numbers with absolute values between 1 and 10^(sf-1) <BR>
* It displays scientific notation for all other numbers (i.e. really big and really small absolute values) <BR>
* <b>note</b>: Its display integers for doubles with integer value <BR>
* @return a nicely formatted number.
*/
public String format(double value) {
StringBuffer buffer = new StringBuffer();
double absValue = Math.abs(value);
if ((absValue > upperCutoff) || (absValue < (1.0 / upperCutoff) && absValue != 0.0)) {
buffer.append(scientificFormat.format(value));
} else {
int numFractionDigits = 0;
if (value != (int)value) {
numFractionDigits = getNumFractionDigits(value);
}
buffer.append(formatDecimal(value, numFractionDigits));
}
if (isPadding) {
int size = fieldWidth - buffer.length();
for (int i =0; i < size; i++) {
buffer.append(' ');
}
}
return buffer.toString();
}
private int getNumFractionDigits(double value) {
value = Math.abs(value);
for (int i =0; i < cutoffTable.length; i++) {
if (value < cutoffTable[i]) return sf-i-1;
}
return sf - 1;
}
private String getScientificPattern(int sf) {
String pattern = "0.";
for (int i =0; i < sf-1; i++) {
pattern += "#";
}
pattern += "E0";
return pattern;
}
}