001/* 002* ParameterFormat -- Provides formatting and parsing for Parameter objects. 003* 004* Copyright (C) 2008-2025, by Joseph A. Huwaldt. All rights reserved. 005* 006* This library is free software; you can redistribute it and/or 007* modify it under the terms of the GNU Lesser General Public 008* License as published by the Free Software Foundation; either 009* version 2 of the License, or (at your option) any later version. 010* 011* This library is distributed in the hope that it will be useful, 012* but WITHOUT ANY WARRANTY; without even the implied warranty of 013* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014* Lesser General Public License for more details. 015* 016* You should have received a copy of the GNU Lesser General Public License 017* along with this program; if not, write to the Free Software 018* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 019* Or visit: http://www.gnu.org/licenses/lgpl.html 020 */ 021package jahuwaldt.js.param; 022 023import java.io.IOException; 024import java.text.ParseException; 025import javax.measure.unit.Unit; 026import javax.measure.unit.UnitFormat; 027import javolution.context.LocalContext; 028import javolution.lang.MathLib; 029import javolution.text.TextFormat; 030import javolution.text.TypeFormat; 031import org.jscience.economics.money.Currency; 032import org.jscience.economics.money.Money; 033 034/** 035 * <p> 036 * This class provides the interface for formatting and parsing {@link 037 * Parameter parameter} instances.</p> 038 * 039 * <p> 040 * Modified by: Joseph A. Huwaldt </p> 041 * 042 * @author Joseph A. Huwaldt Date: November 15, 2008 043 * @version February 23, 2025 044 */ 045public abstract class ParameterFormat extends TextFormat<Parameter<?>> { 046 047 /** 048 * Holds current format. 049 */ 050 private static final LocalContext.Reference<ParameterFormat> CURRENT = new LocalContext.Reference<>( 051 new SimpleFormat()); 052 053 /** 054 * Default constructor. 055 */ 056 protected ParameterFormat() { 057 } 058 059 /** 060 * Returns the current {@link javolution.context.LocalContext local} format (default 061 * <code>ParameterFormat.getSimpleFormatInstance()</code>). 062 * 063 * @return the context local format. 064 * @see #getSimpleFormatInstance 065 */ 066 public static ParameterFormat newInstance() { 067 return CURRENT.get(); 068 } 069 070 /** 071 * Sets the current {@link javolution.context.LocalContext local} format. 072 * 073 * @param format the new format. 074 */ 075 public static void setInstance(ParameterFormat format) { 076 CURRENT.set(format); 077 } 078 079 /** 080 * Returns a simple ParameterFormat for which the value is stated using the current 081 * units; for example <code>"1.34 m"</code>. This format can be used for formatting as 082 * well as for parsing. 083 * 084 * @return A simple ParameterFormat for which the value is stated using the current 085 * units. 086 */ 087 public static ParameterFormat getSimpleFormatInstance() { 088 return new SimpleFormat(); 089 } 090 091 /** 092 * This class represents the simple format with the value and units. 093 */ 094 private static class SimpleFormat extends ParameterFormat { 095 096 /** 097 * Default constructor. 098 */ 099 private SimpleFormat() { 100 } 101 102 @SuppressWarnings("unchecked") 103 @Override 104 public Appendable format(Parameter<?> arg0, Appendable arg1) 105 throws IOException { 106 if (arg0.getUnit() instanceof Currency) 107 return formatMoney((Parameter<Money>) arg0, arg1); 108 double value = arg0.getValue(); 109 double error = MathLib.abs(value) * DOUBLE_RELATIVE_ERROR; 110 int log10Value = (int) MathLib.floor(MathLib.log10(MathLib 111 .abs(value))); 112 int log10Error = (int) MathLib.floor(MathLib.log10(error)); 113 int digits = log10Value - log10Error - 1; // Exact digits. 114 115 boolean scientific = (MathLib.abs(value) >= 1E6) || (MathLib.abs(value) < 1E-6); 116 boolean showZeros = false; 117 TypeFormat.format(value, digits, scientific, showZeros, arg1); 118 arg1.append(' '); 119 return UnitFormat.getInstance().format(arg0.getUnit(), arg1); 120 } 121 122 @Override 123 public Parameter<?> parse(CharSequence arg0, Cursor arg1) { 124 int start = arg1.getIndex(); 125 try { 126 double amount = TypeFormat.parseDouble(arg0, arg1); 127 arg1.skip(' ', arg0); 128 Unit<?> unit = UnitFormat.getInstance().parseProductUnit(arg0, arg1); 129 return Parameter.valueOf(amount, unit); 130 131 } catch (ParseException e) { 132 arg1.setIndex(start); 133 arg1.setErrorIndex(e.getErrorOffset()); 134 return null; 135 } 136 } 137 } 138 139 /** 140 * Provides custom formatting for money measurements. 141 */ 142 private static Appendable formatMoney(Parameter<Money> arg0, Appendable arg1) 143 throws IOException { 144 Currency currency = (Currency) arg0.getUnit(); 145 int fraction = currency.getDefaultFractionDigits(); 146 if (fraction == 0) { 147 long amount = arg0.longValue(currency); 148 TypeFormat.format(amount, arg1); 149 } else if (fraction == 2) { 150 long amount = MathLib.round(arg0.doubleValue(arg0.getUnit()) * 100); 151 TypeFormat.format(amount / 100, arg1); 152 arg1.append('.'); 153 arg1.append((char) ('0' + (amount % 100) / 10)); 154 arg1.append((char) ('0' + (amount % 10))); 155 } else { 156 throw new UnsupportedOperationException(); 157 } 158 arg1.append(' '); 159 return UnitFormat.getInstance().format(currency, arg1); 160 } 161 162 static final double DOUBLE_RELATIVE_ERROR = MathLib.pow(2, -53); 163 164}