001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2016, Jean-Marie Dautelle, Werner Keil, V2COM. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package tec.units.ri.format; 031 032import java.io.IOException; 033 034import javax.measure.MeasurementException; 035import javax.measure.Quantity; 036import javax.measure.Unit; 037import javax.measure.format.ParserException; 038import tec.units.ri.AbstractQuantity; 039import tec.units.ri.AbstractUnit; 040import tec.units.ri.quantity.NumberQuantity; 041import tec.uom.lib.common.function.Parser; 042 043/** 044 * <p> 045 * This class provides the interface for formatting and parsing {@link Quantity quantities}. 046 * </p> 047 * 048 * <p> 049 * Instances of this class should be able to format quantities stated in {@link CompoundUnit}. See {@link #formatCompound formatCompound(...)}. 050 * </p> 051 * 052 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 053 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 054 * @version 0.8.4, $Date: 2016-04-20 $ 055 */ 056@SuppressWarnings("rawtypes") 057public abstract class QuantityFormat implements Parser<CharSequence, Quantity> { 058 059 /** 060 * 061 */ 062 // private static final long serialVersionUID = -4628006924354248662L; 063 064 /** 065 * Holds the default format instance. 066 */ 067 private static final QuantityFormat DEFAULT = new Standard(); 068 069 /** 070 * Holds the Number-Space-Unit format instance. 071 */ 072 // private static final QuantityFormat NUM_SPACE = new NumberSpaceUnit(NumberFormat.getInstance(), SimpleUnitFormat.getInstance()); 073 074 // TODO use it as an option (after fixing parse()) 075 076 /** 077 * Returns the quantity format for the default locale. The default format assumes the quantity is composed of a decimal number and a {@link Unit} 078 * separated by whitespace(s). 079 * 080 * @return <code>MeasureFormat.getInstance(NumberFormat.getInstance(), UnitFormat.getInstance())</code> 081 */ 082 public static QuantityFormat getInstance() { 083 return DEFAULT; 084 } 085 086 /** 087 * Formats the specified quantity into an <code>Appendable</code>. 088 * 089 * @param quantity 090 * the quantity to format. 091 * @param dest 092 * the appendable destination. 093 * @return the specified <code>Appendable</code>. 094 * @throws IOException 095 * if an I/O exception occurs. 096 */ 097 public abstract Appendable format(Quantity<?> quantity, Appendable dest) throws IOException; 098 099 /** 100 * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the index 101 * of the <code>cursor</code> argument is updated to the index after the last character used. 102 * 103 * @param csq 104 * the <code>CharSequence</code> to parse. 105 * @param index 106 * the current parsing index. 107 * @return the object parsed from the specified character sub-sequence. 108 * @throws IllegalArgumentException 109 * if any problem occurs while parsing the specified character sequence (e.g. illegal syntax). 110 */ 111 abstract Quantity<?> parse(CharSequence csq, int index) throws IllegalArgumentException, ParserException; 112 113 /** 114 * Convenience method equivalent to {@link #format(AbstractQuantity, Appendable)} except it does not raise an IOException. 115 * 116 * @param quantity 117 * the quantity to format. 118 * @param dest 119 * the appendable destination. 120 * @return the specified <code>StringBuilder</code>. 121 */ 122 public final StringBuilder format(Quantity<?> q, StringBuilder dest) { 123 try { 124 return (StringBuilder) this.format(q, (Appendable) dest); 125 } catch (IOException ex) { 126 throw new MeasurementException(ex); // Should not happen. 127 } 128 } 129 130 /** 131 * Formats an object to produce a string. This is equivalent to <blockquote> {@link #format(Unit, StringBuilder) format}<code>(unit, 132 * new StringBuilder()).toString();</code> </blockquote> 133 * 134 * @param obj 135 * The object to format 136 * @return Formatted string. 137 * @exception IllegalArgumentException 138 * if the Format cannot format the given object 139 */ 140 public final String format(Quantity q) { 141 if (q instanceof AbstractQuantity) { 142 return format((AbstractQuantity<?>) q, new StringBuilder()).toString(); 143 } else { 144 return (this.format(q, new StringBuilder())).toString(); 145 } 146 } 147 148 static int getFractionDigitsCount(double d) { 149 if (d >= 1) { // we only need the fraction digits 150 d = d - (long) d; 151 } 152 if (d == 0) { // nothing to count 153 return 0; 154 } 155 d *= 10; // shifts 1 digit to left 156 int count = 1; 157 while (d - (long) d != 0) { // keeps shifting until there are no more 158 // fractions 159 d *= 10; 160 count++; 161 } 162 return count; 163 } 164 165 // Holds standard implementation. 166 private static final class Standard extends QuantityFormat { 167 168 /** 169 * 170 */ 171 // private static final long serialVersionUID = 2758248665095734058L; 172 173 @Override 174 public Appendable format(Quantity q, Appendable dest) throws IOException { 175 Unit unit = q.getUnit(); 176 // if (unit instanceof CompoundUnit) 177 // return formatCompound(q.doubleValue(unit), 178 // (CompoundUnit) unit, dest); 179 // else { 180 181 Number number = q.getValue(); 182 dest.append(number.toString()); 183 // } 184 if (q.getUnit().equals(AbstractUnit.ONE)) 185 return dest; 186 dest.append(' '); 187 return SimpleUnitFormat.getInstance().format(unit, dest); 188 // } 189 } 190 191 @SuppressWarnings("unchecked") 192 @Override 193 Quantity<?> parse(CharSequence csq, int index) throws ParserException { 194 int startDecimal = index; // cursor.getIndex(); 195 while ((startDecimal < csq.length()) && Character.isWhitespace(csq.charAt(startDecimal))) { 196 startDecimal++; 197 } 198 int endDecimal = startDecimal + 1; 199 while ((endDecimal < csq.length()) && !Character.isWhitespace(csq.charAt(endDecimal))) { 200 endDecimal++; 201 } 202 Double decimal = new Double(csq.subSequence(startDecimal, endDecimal).toString()); 203 // cursor.setIndex(endDecimal + 1); 204 int startUnit = endDecimal + 1;// csq.toString().indexOf(' ') + 1; 205 Unit unit = SimpleUnitFormat.getInstance().parse(csq, startUnit); 206 return NumberQuantity.of(decimal.doubleValue(), unit); 207 } 208 209 public Quantity<?> parse(CharSequence csq) throws ParserException { 210 return parse(csq, 0); 211 } 212 } 213}