/*
 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
 * Copyright (C) 2006 - JScience (http://jscience.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package org.jscience.physics.amount;

import java.io.IOException;
import java.text.ParseException;

import org.jscience.economics.money.Currency;
import org.jscience.economics.money.Money;

import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;

import javolution.lang.MathLib;
import javolution.text.Text;
import javolution.text.TextBuilder;
import javolution.text.TextFormat;
import javolution.text.TypeFormat;
import javolution.context.LocalContext;

//@RETROWEAVER import javolution.text.Appendable;

/**
 * 

This class provides the interface for formatting and parsing {@link * Amount measures} instances. For example:[code] * // Display measurements using unscaled units (e.g. base units or alternate units). * AmountFormat.setInstance(new AmountFormat() { // Context local. * public Appendable format(Amount m, Appendable a) throws IOException { * Unit u = m.getUnit(); * if (u instanceof TransformedUnit) * u = ((TransformedUnit)u).getParentUnit(); * return AmountFormat.getPlusMinusErrorInstance(2).format(m.to(u), a); * } * public Amount parse(CharSequence csq, Cursor c) { * return AmountFormat.getPlusMinusErrorInstance(2).parse(csq, c); * } * });[/code] *

* * @author Jean-Marie Dautelle * @version 3.0, February 21, 2006 */ public abstract class AmountFormat extends TextFormat> { /** * Holds current format. */ private static final LocalContext.Reference CURRENT = new LocalContext.Reference( new PlusMinusError(2)); /** * Default constructor. */ protected AmountFormat() { } /** * Returns the current {@link javolution.context.LocalContext local} * format (default AmountFormat.getPlusMinusErrorInstance(2)). * * @return the context local format. * @see #getPlusMinusErrorInstance(int) */ public static AmountFormat getInstance() { return CURRENT.get(); } /** * Sets the current {@link javolution.context.LocalContext local} format. * * @param format the new format. */ public static void setInstance(AmountFormat format) { CURRENT.set(format); } /** * Returns a format for which the error (if present) is stated using * the '±' character; for example "(1.34 ± 0.01) m". * This format can be used for formatting as well as for parsing. * * @param digitsInError the maximum number of digits in error. */ public static AmountFormat getPlusMinusErrorInstance(int digitsInError) { return new PlusMinusError(digitsInError); } /** * Returns a format for which the error is represented by an integer * value in brackets; for example "1.3456[20] m" * is equivalent to "1.3456 ± 0.0020 m". * This format can be used for formatting as well as for parsing. * * @param digitsInError the maximum number of digits in error. */ public static AmountFormat getBracketErrorInstance(int digitsInError) { return new BracketError(digitsInError); } /** * Returns a format for which only digits guaranteed to be exact are * written out. In other words, the error is always on the * last digit and less than the last digit weight. For example, * "1.34 m" means a length between 1.32 m and * 1.35 m. This format can be used for formatting only. */ public static AmountFormat getExactDigitsInstance() { return new ExactDigitsOnly(); } /** * This class represents the plus minus error format. */ private static class PlusMinusError extends AmountFormat { /** * Holds the number of digits in error. */ private int _errorDigits; /** * Creates a plus-minus error format having the specified * number of digits in error. * * @param errorDigits the number of digits in error. */ private PlusMinusError(int errorDigits) { _errorDigits = errorDigits; } @SuppressWarnings("unchecked") @Override public Appendable format(Amount arg0, Appendable arg1) throws IOException { if (arg0.getUnit() instanceof Currency) return formatMoney(arg0, arg1); if (arg0.isExact()) { TypeFormat.format(arg0.getExactValue(), arg1); arg1.append(' '); return UnitFormat.getInstance().format(arg0.getUnit(), arg1); } double value = arg0.getEstimatedValue(); double error = arg0.getAbsoluteError(); int log10Value = (int) MathLib.floor(MathLib.log10(MathLib .abs(value))); int log10Error = (int) MathLib.floor(MathLib.log10(error)); int digits = log10Value - log10Error - 1; // Exact digits. digits = MathLib.max(1, digits + _errorDigits); boolean scientific = (MathLib.abs(value) >= 1E6) || (MathLib.abs(value) < 1E-6); boolean showZeros = false; arg1.append('('); TypeFormat.format(value, digits, scientific, showZeros, arg1); arg1.append(" ± "); scientific = (MathLib.abs(error) >= 1E6) || (MathLib.abs(error) < 1E-6); showZeros = true; TypeFormat.format(error, _errorDigits, scientific, showZeros, arg1); arg1.append(") "); return UnitFormat.getInstance().format(arg0.getUnit(), arg1); } @Override public Amount parse(CharSequence arg0, Cursor arg1) { int start = arg1.getIndex(); try { arg1.skip('(', arg0); long value = TypeFormat.parseLong(arg0, 10, arg1); if (arg0.charAt(arg1.getIndex()) == ' ') { // Exact! arg1.skip(' ', arg0); Unit unit = UnitFormat.getInstance().parseProductUnit( arg0, arg1); return Amount.valueOf(value, unit); } arg1.setIndex(start); double amount = TypeFormat.parseDouble(arg0, arg1); arg1.skip(' ', arg0); double error = 0; if (arg0.charAt(arg1.getIndex()) == '±') { // Error specified. arg1.skip('±', arg0); arg1.skip(' ', arg0); error = TypeFormat.parseDouble(arg0, arg1); } arg1.skip(')', arg0); arg1.skip(' ', arg0); Unit unit = UnitFormat.getInstance().parseProductUnit(arg0, arg1); return Amount.valueOf(amount, error, unit); } catch (ParseException e) { arg1.setIndex(start); arg1.setErrorIndex(e.getErrorOffset()); return null; } } } /** * This class represents the bracket error format. */ private static class BracketError extends AmountFormat { /** * Holds the number of digits in error. */ private int _errorDigits; /** * Creates a bracket error format having the specified * number of digits in error. * * @param bracket the number of digits in error. */ private BracketError(int errorDigits) { _errorDigits = errorDigits; } @SuppressWarnings("unchecked") @Override public Appendable format(Amount arg0, Appendable arg1) throws IOException { if (arg0.getUnit() instanceof Currency) return formatMoney(arg0, arg1); if (arg0.isExact()) { TypeFormat.format(arg0.getExactValue(), arg1); arg1.append(' '); return UnitFormat.getInstance().format(arg0.getUnit(), arg1); } double value = arg0.getEstimatedValue(); double error = arg0.getAbsoluteError(); int log10Value = (int) MathLib.floor(MathLib.log10(MathLib .abs(value))); int log10Error = (int) MathLib.floor(MathLib.log10(error)); int digits = log10Value - log10Error - 1; // Exact digits. digits = MathLib.max(1, digits + _errorDigits); boolean scientific = (MathLib.abs(value) >= 1E6) || (MathLib.abs(value) < 1E-6); boolean showZeros = true; TextBuilder tb = TextBuilder.newInstance(); TypeFormat.format(value, digits, scientific, showZeros, tb); int endMantissa = 0; for (; endMantissa < tb.length(); endMantissa++) { if (tb.charAt(endMantissa) == 'E') break; } int bracketError = (int) (error * MathLib.toDoublePow10(1, -log10Error + _errorDigits - 1)); tb.insert(endMantissa, Text.valueOf('[').plus( Text.valueOf(bracketError)).plus(']')); arg1.append(tb); arg1.append(' '); return UnitFormat.getInstance().format(arg0.getUnit(), arg1); } @Override public Amount parse(CharSequence arg0, Cursor arg1) { // TBD throw new UnsupportedOperationException("Not supported yet"); } } /** * This class represents the exact digits only format. */ private static class ExactDigitsOnly extends AmountFormat { /** * Default constructor. */ private ExactDigitsOnly() { } @SuppressWarnings("unchecked") @Override public Appendable format(Amount arg0, Appendable arg1) throws IOException { if (arg0.getUnit() instanceof Currency) return formatMoney(arg0, arg1); if (arg0.isExact()) { TypeFormat.format(arg0.getExactValue(), arg1); arg1.append(' '); return UnitFormat.getInstance().format(arg0.getUnit(), arg1); } double value = arg0.getEstimatedValue(); double error = arg0.getAbsoluteError(); int log10Value = (int) MathLib.floor(MathLib.log10(MathLib .abs(value))); int log10Error = (int) MathLib.floor(MathLib.log10(error)); int digits = log10Value - log10Error - 1; // Exact digits. boolean scientific = (MathLib.abs(value) >= 1E6) || (MathLib.abs(value) < 1E-6); boolean showZeros = true; TypeFormat.format(value, digits, scientific, showZeros, arg1); arg1.append(' '); return UnitFormat.getInstance().format(arg0.getUnit(), arg1); } @Override public Amount parse(CharSequence arg0, Cursor arg1) { throw new UnsupportedOperationException( "This format should not be used for parsing " + "(not enough information on the error"); } } /** * Provides custom formatting for money measurements. */ private static Appendable formatMoney(Amount arg0, Appendable arg1) throws IOException { Currency currency = (Currency) arg0.getUnit(); int fraction = currency.getDefaultFractionDigits(); if (fraction == 0) { long amount = arg0.longValue(currency); TypeFormat.format(amount, arg1); } else if (fraction == 2) { long amount = MathLib.round(arg0.doubleValue(arg0.getUnit()) * 100); TypeFormat.format(amount / 100, arg1); arg1.append('.'); arg1.append((char) ('0' + (amount % 100) / 10)); arg1.append((char) ('0' + (amount % 10))); } else { throw new UnsupportedOperationException(); } arg1.append(' '); return UnitFormat.getInstance().format(currency, arg1); } }

Ads help maintain this website.