001/* 002 * Units of Measurement Implementation for Java SE 003 * Copyright (c) 2005-2017, 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.uom.se.format; 031 032import java.io.IOException; 033import java.math.BigDecimal; 034import java.text.FieldPosition; 035import java.text.Format; 036import java.text.NumberFormat; 037import java.text.ParsePosition; 038 039import javax.measure.Quantity; 040import javax.measure.Unit; 041import javax.measure.format.ParserException; 042import javax.measure.format.UnitFormat; 043 044import tec.uom.lib.common.function.Parser; 045import tec.uom.se.AbstractQuantity; 046import tec.uom.se.ComparableQuantity; 047 048/** 049 * <p> 050 * This class provides the interface for formatting and parsing {@link AbstractQuantity quantities}. 051 * </p> 052 * 053 * <p> 054 * Instances of this class should be able to format quantities stated in {@link CompoundUnit}. See {@link #formatCompound formatCompound(...)}. 055 * </p> 056 * 057 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 058 * @author <a href="mailto:units@catmedia.us">Werner Keil</a> 059 * @version 1.0, $Date: 2016-10-11 $ 060 * @since 1.0 061 * 062 */ 063@SuppressWarnings("rawtypes") 064public abstract class QuantityFormat extends Format implements Parser<CharSequence, ComparableQuantity> { 065 066 /** 067 * 068 */ 069 private static final long serialVersionUID = -4628006924354248662L; 070 071 /** 072 * Holds the localized format instance. 073 */ 074 private static final NumberSpaceQuantityFormat LOCAL = new NumberSpaceQuantityFormat(NumberFormat.getInstance(), LocalUnitFormat.getInstance()); 075 076 /** 077 * Holds the default format instance. 078 */ 079 private static final DefaultQuantityFormat DEFAULT = new DefaultQuantityFormat(); 080 081 /** 082 * Returns the quantity format for the default locale. The default format assumes the quantity is composed of a decimal number and a {@link Unit} 083 * separated by whitespace(s). 084 * 085 * @return <code>MeasureFormat.getInstance(NumberFormat.getInstance(), UnitFormat.getInstance())</code> 086 */ 087 public static QuantityFormat getInstance() { 088 return DEFAULT; 089 } 090 091 /** 092 * Returns the quantity format using the specified number format and unit format (the number and unit are separated by one space). 093 * 094 * @param numberFormat 095 * the number format. 096 * @param unitFormat 097 * the unit format. 098 * @return the corresponding format. 099 */ 100 public static QuantityFormat getInstance(NumberFormat numberFormat, UnitFormat unitFormat) { 101 return new NumberSpaceQuantityFormat(numberFormat, unitFormat); 102 } 103 104 /** 105 * Returns the culture invariant format based upon {@link BigDecimal} canonical format and the {@link UnitFormat#getStandardInstance() standard} 106 * unit format. This format <b>is not</b> locale-sensitive and can be used for unambiguous electronic communication of quantities together with 107 * their units without loss of information. For example: <code>"1.23456789 kg.m/s2"</code> returns 108 * <code>Quantities.getQuantity(new BigDecimal("1.23456789"), AbstractUnit.parse("kg.m/s2")));</code> 109 * 110 * @param style 111 * the format style to apply. 112 * @return the desired format. 113 */ 114 public static QuantityFormat getInstance(FormatBehavior style) { 115 switch (style) { 116 case LOCALE_NEUTRAL: 117 return DEFAULT; 118 case LOCALE_SENSITIVE: 119 return LOCAL; 120 default: 121 return DEFAULT; 122 } 123 } 124 125 /** 126 * Formats the specified quantity into an <code>Appendable</code>. 127 * 128 * @param quantity 129 * the quantity to format. 130 * @param dest 131 * the appendable destination. 132 * @return the specified <code>Appendable</code>. 133 * @throws IOException 134 * if an I/O exception occurs. 135 */ 136 public abstract Appendable format(Quantity<?> quantity, Appendable dest) throws IOException; 137 138 /** 139 * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the index 140 * of the <code>cursor</code> argument is updated to the index after the last character used. 141 * 142 * @param csq 143 * the <code>CharSequence</code> to parse. 144 * @param cursor 145 * the cursor holding the current parsing index. 146 * @return the object parsed from the specified character sub-sequence. 147 * @throws IllegalArgumentException 148 * if any problem occurs while parsing the specified character sequence (e.g. illegal syntax). 149 */ 150 public abstract ComparableQuantity<?> parse(CharSequence csq, ParsePosition cursor) throws IllegalArgumentException, ParserException; 151 152 /** 153 * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the index 154 * of the <code>cursor</code> argument is updated to the index after the last character used. 155 * 156 * @param csq 157 * the <code>CharSequence</code> to parse. 158 * @param cursor 159 * the cursor holding the current parsing index. 160 * @return the object parsed from the specified character sub-sequence. 161 * @throws IllegalArgumentException 162 * if any problem occurs while parsing the specified character sequence (e.g. illegal syntax). 163 */ 164 @Override 165 public abstract ComparableQuantity<?> parse(CharSequence csq) throws IllegalArgumentException, ParserException; 166 167 /** 168 * Parses a portion of the specified <code>CharSequence</code> from the specified position to produce an object. If parsing succeeds, then the index 169 * of the <code>cursor</code> argument is updated to the index after the last character used. 170 * 171 * @param csq 172 * the <code>CharSequence</code> to parse. 173 * @param index 174 * the current parsing index. 175 * @return the object parsed from the specified character sub-sequence. 176 * @throws IllegalArgumentException 177 * if any problem occurs while parsing the specified character sequence (e.g. illegal syntax). 178 */ 179 abstract ComparableQuantity<?> parse(CharSequence csq, int index) throws IllegalArgumentException, ParserException; 180 181 @Override 182 public final StringBuffer format(Object obj, final StringBuffer toAppendTo, FieldPosition pos) { 183 if (!(obj instanceof AbstractQuantity<?>)) 184 throw new IllegalArgumentException("obj: Not an instance of Quantity"); 185 if ((toAppendTo == null) || (pos == null)) 186 throw new NullPointerException(); 187 try { 188 return (StringBuffer) format((AbstractQuantity<?>) obj, toAppendTo); 189 } catch (IOException ex) { 190 throw new Error(ex); // Cannot happen. 191 } 192 } 193 194 @Override 195 public final Quantity<?> parseObject(String source, ParsePosition pos) { 196 try { 197 return parse(source, pos); 198 } catch (IllegalArgumentException | ParserException e) { 199 return null; 200 } 201 202 } 203 204 /** 205 * Convenience method equivalent to {@link #format(AbstractQuantity, Appendable)} except it does not raise an IOException. 206 * 207 * @param quantity 208 * the quantity to format. 209 * @param dest 210 * the appendable destination. 211 * @return the specified <code>StringBuilder</code>. 212 */ 213 public final StringBuilder format(AbstractQuantity<?> quantity, StringBuilder dest) { 214 try { 215 return (StringBuilder) this.format(quantity, (Appendable) dest); 216 } catch (IOException ex) { 217 throw new RuntimeException(ex); // Should not happen. 218 } 219 } 220 221}