001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 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-385, Indriya nor the names of their 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 tech.units.indriya.format; 031 032import static tech.units.indriya.format.FormatBehavior.LOCALE_NEUTRAL; 033 034import java.io.IOException; 035import java.math.BigDecimal; 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 tech.units.indriya.AbstractUnit; 045import tech.units.indriya.ComparableQuantity; 046import tech.units.indriya.quantity.Quantities; 047 048@SuppressWarnings({ "rawtypes", "unchecked" }) 049public class NumberSpaceQuantityFormat extends AbstractQuantityFormat { 050 051 /** 052 * Holds the default format instance. 053 */ 054 private static final NumberSpaceQuantityFormat DEFAULT = new NumberSpaceQuantityFormat(NumberFormat.getInstance(), 055 EBNFUnitFormat.getInstance()); 056 057 /** 058 * Holds the localized format instance. 059 */ 060 private static final NumberSpaceQuantityFormat LOCAL = new NumberSpaceQuantityFormat(NumberFormat.getInstance(), 061 LocalUnitFormat.getInstance()); 062 063 /** 064 * 065 */ 066 private static final long serialVersionUID = 3546952599885869402L; 067 068 private final NumberFormat numberFormat; 069 070 private final UnitFormat unitFormat; 071 072 NumberSpaceQuantityFormat(NumberFormat numberFormat, UnitFormat unitFormat) { 073 this.numberFormat = numberFormat; 074 this.unitFormat = unitFormat; 075 } 076 077 static int getFractionDigitsCount(double d) { 078 if (d >= 1) { // we only need the fraction digits 079 d = d - (long) d; 080 } 081 if (d == 0) { // nothing to count 082 return 0; 083 } 084 d *= 10; // shifts 1 digit to left 085 int count = 1; 086 while (d - (long) d != 0) { // keeps shifting until there are no more 087 // fractions 088 d *= 10; 089 count++; 090 } 091 return count; 092 } 093 094 @Override 095 public Appendable format(Quantity<?> quantity, Appendable dest) throws IOException { 096 int fract = 0; 097 if (quantity != null && quantity.getValue() != null) { 098 fract = getFractionDigitsCount(quantity.getValue().doubleValue()); 099 } 100 if (fract > 1) { 101 numberFormat.setMaximumFractionDigits(fract + 1); 102 } 103 dest.append(numberFormat.format(quantity.getValue())); 104 if (quantity.getUnit().equals(AbstractUnit.ONE)) 105 return dest; 106 dest.append(' '); 107 return unitFormat.format(quantity.getUnit(), dest); 108 } 109 110 @Override 111 public ComparableQuantity<?> parse(CharSequence csq, ParsePosition cursor) 112 throws IllegalArgumentException, ParserException { 113 String str = csq.toString(); 114 Number number = numberFormat.parse(str, cursor); 115 if (number == null) 116 throw new IllegalArgumentException("Number cannot be parsed"); 117 118 Unit unit = unitFormat.parse(csq); 119 return Quantities.getQuantity(number.longValue(), unit); 120 } 121 122 @Override 123 ComparableQuantity<?> parse(CharSequence csq, int index) 124 throws IllegalArgumentException, ParserException { 125 return parse(csq, new ParsePosition(index)); 126 } 127 128 @Override 129 public ComparableQuantity<?> parse(CharSequence csq) throws IllegalArgumentException, ParserException { 130 return parse(csq, 0); 131 } 132 133 /** 134 * Returns the culture invariant format based upon {@link BigDecimal} canonical 135 * format and the {@link UnitFormat#getStandardInstance() standard} unit format. 136 * This format <b>is not</b> locale-sensitive and can be used for unambiguous 137 * electronic communication of quantities together with their units without loss 138 * of information. For example: <code>"1.23456789 kg.m/s2"</code> returns 139 * <code>Quantities.getQuantity(new BigDecimal("1.23456789"), AbstractUnit.parse("kg.m/s2")));</code> 140 * 141 * @param style 142 * the format style to apply. 143 * @return the desired format. 144 */ 145 public static NumberSpaceQuantityFormat getInstance(FormatBehavior style) { 146 switch (style) { 147 case LOCALE_NEUTRAL: 148 return DEFAULT; 149 case LOCALE_SENSITIVE: 150 return LOCAL; 151 default: 152 return DEFAULT; 153 } 154 } 155 156 /** 157 * Returns the default format. 158 * @return the desired format. 159 */ 160 public static NumberSpaceQuantityFormat getInstance() { 161 return getInstance(LOCALE_NEUTRAL); 162 } 163 164 /** 165 * Returns the quantity format using the specified number format and unit format 166 * (the number and unit are separated by one space). 167 * 168 * @param numberFormat 169 * the number format. 170 * @param unitFormat 171 * the unit format. 172 * @return the corresponding format. 173 */ 174 public static NumberSpaceQuantityFormat getInstance(NumberFormat numberFormat, UnitFormat unitFormat) { 175 return new NumberSpaceQuantityFormat(numberFormat, unitFormat); 176 } 177}