tec.units.ri.format.QuantityFormat Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unit-ri Show documentation
Show all versions of unit-ri Show documentation
Unit Standard (JSR 363) Reference Implementation.
/*
* Unit-API - Units of Measurement API for Java
* Copyright (c) 2005-2015, Jean-Marie Dautelle, Werner Keil, V2COM.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package tec.units.ri.format;
import java.io.IOException;
import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.format.Parser;
import javax.measure.format.ParserException;
import javax.measure.format.UnitFormat;
import tec.units.ri.AbstractQuantity;
import tec.units.ri.AbstractUnit;
import tec.units.ri.internal.format.l10n.NumberFormat;
import tec.units.ri.quantity.NumberQuantity;
import tec.units.ri.unit.Units;
/**
* This class provides the interface for formatting and parsing {@link AbstractQuantity
* measurements}.
*
* Instances of this class should be able to format measurements stated in
* {@link CompoundUnit}. See {@link #formatCompound formatCompound(...)}.
*
*
* @author Jean-Marie Dautelle
* @author Werner Keil
* @version 0.6.2, $Date: 2015-07-05 $
*/
@SuppressWarnings("rawtypes")
public abstract class QuantityFormat implements Parser {
/**
*
*/
// private static final long serialVersionUID = -4628006924354248662L;
/**
* Holds the default format instance.
*/
private static final NumberSpaceUnit DEFAULT = new NumberSpaceUnit(
NumberFormat.getInstance(), EBNFUnitFormat.getInstance());
/**
* Holds the standard format instance.
*/
private static final Standard STANDARD = new Standard();
/**
* Returns the measure format for the default locale. The default format
* assumes the measure is composed of a decimal number and a {@link Unit}
* separated by whitespace(s).
*
* @return MeasureFormat.getInstance(NumberFormat.getInstance(), UnitFormat.getInstance())
*/
public static QuantityFormat getInstance() {
return DEFAULT;
}
// /**
// * Returns the measure format using the specified number format and unit
// * format (the number and unit are separated by one space).
// *
// * @param numberFormat the number format.
// * @param unitFormat the unit format.
// * @return the corresponding format.
// */
// public static QuantityFormat getInstance(NumberFormat numberFormat,
// UnitFormat unitFormat) {
// return new NumberSpaceUnit(numberFormat, unitFormat);
// }
/**
* Returns the culture invariant format based upon {@link Number}
* canonical format and the {@link UnitFormat#current() standard} unit
* format. This format is not locale-sensitive and can be used for
* unambiguous electronic communication of quantities together with their
* units without loss of information. For example:
* "1.23456789 kg.m/s2"
returns
* Quantities.getQuantity(new Double(1.23456789d), AbstractUnit.parse("kg.m/s2")));
*
* @param style the format style to apply.
* @return the desired format.
*/
public static QuantityFormat getInstance(FormatBehavior style) {
switch (style) {
case LOCALE_NEUTRAL:
return STANDARD;
case LOCALE_SENSITIVE:
return DEFAULT;
default:
return DEFAULT;
}
}
/**
* Formats the specified measure into an Appendable
.
*
* @param measure the measure to format.
* @param dest the appendable destination.
* @return the specified Appendable
.
* @throws IOException if an I/O exception occurs.
*/
public abstract Appendable format(AbstractQuantity> measure, Appendable dest)
throws IOException;
/**
* Parses a portion of the specified CharSequence
from the
* specified position to produce an object. If parsing succeeds, then the
* index of the cursor
argument is updated to the index after
* the last character used.
*
* @param csq the CharSequence
to parse.
* @param index the current parsing index.
* @return the object parsed from the specified character sub-sequence.
* @throws IllegalArgumentException
* if any problem occurs while parsing the specified character
* sequence (e.g. illegal syntax).
*/
abstract AbstractQuantity> parse(CharSequence csq, int index)
throws IllegalArgumentException, ParserException;
/**
* Parses a portion of the specified CharSequence
from the
* specified position to produce an object. If parsing succeeds, then the
* index of the cursor
argument is updated to the index after
* the last character used.
*
* @param csq the CharSequence
to parse.
* @param cursor the cursor holding the current parsing index.
* @return the object parsed from the specified character sub-sequence.
* @throws IllegalArgumentException
* if any problem occurs while parsing the specified character
* sequence (e.g. illegal syntax).
*/
/*public abstract AbstractQuantity> parse(CharSequence csq)
throws IllegalArgumentException, ParserException; */
/**
* Formats the specified value using {@link CompoundUnit} compound units}.
* The default implementation is locale sensitive and does not use space to
* separate units. For example:[code]
* Unit FOOT_INCH = FOOT.compound(INCH);
* Measure height = Measure.valueOf(1.81, METER);
* System.out.println(height.to(FOOT_INCH));
*
* > 5ft11,26in // French Local
*
* Unit DMS = DEGREE_ANGLE.compound(MINUTE_ANGLE).compound(SECOND_ANGLE);
* Measure rotation = Measure.valueOf(35.857497, DEGREE_ANGLE);
* System.out.println(rotation.to(DMS));
*
* > 35°51'26,989" // French Local
* [/code]
*
* @param value the value to format using compound units.
* @param unit the compound unit.
* @param dest the appendable destination.
* @return the specified Appendable
.
* @throws IOException if an I/O exception occurs.
*/
// @SuppressWarnings("unchecked")
// protected Appendable formatCompound(double value, CompoundUnit> unit,
// Appendable dest) throws IOException {
// Unit high = unit.getHigh();
// Unit low = unit.getLow(); // The unit in which the value is stated.
// long highValue = (long) low.getConverterTo(high).convert(value);
// double lowValue = value - high.getConverterTo(low).convert(highValue);
// if (high instanceof CompoundUnit)
// formatCompound(highValue, (CompoundUnit) high, dest);
// else {
// dest.append(DEFAULT._numberFormat.format(highValue));
// DEFAULT._unitFormat.format(high, dest);
// }
// dest.append(DEFAULT._numberFormat.format(lowValue));
// return DEFAULT._unitFormat.format(low, dest);
// }
/* public final StringBuffer format(Object obj, final StringBuffer toAppendTo,
FieldPosition pos) {
if (!(obj instanceof AbstractQuantity>))
throw new IllegalArgumentException(
"obj: Not an instance of Measure");
if ((toAppendTo == null) || (pos == null))
throw new NullPointerException();
try {
return (StringBuffer) format((AbstractQuantity>) obj,
(Appendable) toAppendTo);
} catch (IOException ex) {
throw new Error(ex); // Cannot happen.
}
} */
/* final AbstractQuantity> parseObject(String source, ParsePosition pos) {
try {
return parse(source, pos);
} catch (IllegalArgumentException | ParserException e) {
return null; // Unfortunately the message why the parsing failed
} // is lost; but we have to follow the Format spec.
} */
/**
* Convenience method equivalent to {@link #format(AbstractQuantity, Appendable)}
* except it does not raise an IOException.
*
* @param measure the measure to format.
* @param dest the appendable destination.
* @return the specified StringBuilder
.
*/
public final StringBuilder format(AbstractQuantity> measure, StringBuilder dest) {
try {
return (StringBuilder) this.format(measure, (Appendable) dest);
} catch (IOException ex) {
throw new RuntimeException(ex); // Should not happen.
}
}
// Holds default implementation.
private static final class NumberSpaceUnit extends QuantityFormat {
private final NumberFormat numberFormat;
private final UnitFormat unitFormat;
private NumberSpaceUnit(NumberFormat numberFormat, UnitFormat unitFormat) {
this.numberFormat = numberFormat;
this.unitFormat = unitFormat;
}
@Override
public Appendable format(AbstractQuantity> quantity, Appendable dest)
throws IOException {
// Unit unit = measure.getUnit();
// if (unit instanceof CompoundUnit)
// return formatCompound(measure.doubleValue(unit),
// (CompoundUnit) unit, dest);
// else {
dest.append(numberFormat.format(quantity.getValue()));
if (quantity.getUnit().equals(Units.ONE))
return dest;
dest.append(' ');
return unitFormat.format(quantity.getUnit(), dest);
// }
}
@SuppressWarnings("unchecked")
@Override
AbstractQuantity> parse(CharSequence csq, int index)
throws IllegalArgumentException, ParserException {
String str = csq.toString();
Number number = numberFormat.parse(str);
if (number == null)
throw new IllegalArgumentException("Number cannot be parsed");
Unit unit = unitFormat.parse(csq);
if (number instanceof Long)
return NumberQuantity.of(number.longValue(), unit);
else if (number instanceof Double)
return NumberQuantity.of(number.doubleValue(), unit);
else if (number instanceof Integer)
return NumberQuantity.of(number.intValue(), unit);
else
throw new UnsupportedOperationException("Number of type "
+ number.getClass() + " are not supported");
}
public AbstractQuantity> parse(CharSequence csq) throws IllegalArgumentException, ParserException {
return parse(csq, 0);
}
// private static final long serialVersionUID = 1L;
}
// Holds standard implementation.
private static final class Standard extends QuantityFormat {
/**
*
*/
// private static final long serialVersionUID = 2758248665095734058L;
@Override
public Appendable format(AbstractQuantity measure, Appendable dest)
throws IOException {
Unit unit = measure.getUnit();
// if (unit instanceof CompoundUnit)
// return formatCompound(measure.doubleValue(unit),
// (CompoundUnit) unit, dest);
// else {
// if (measure.isBig()) { // TODO SE only
// BigDecimal decimal = measure.decimalValue(unit,
// MathContext.UNLIMITED);
// dest.append(decimal.toString());
// } else {
Number number = measure.getValue();
dest.append(number.toString());
// }
if (measure.getUnit().equals(Units.ONE))
return dest;
dest.append(' ');
return EBNFUnitFormat.getInstance().format(unit, dest);
// }
}
@SuppressWarnings("unchecked")
@Override
AbstractQuantity> parse(CharSequence csq, int index)
throws ParserException {
int startDecimal = index; //cursor.getIndex();
while ((startDecimal < csq.length())
&& Character.isWhitespace(csq.charAt(startDecimal))) {
startDecimal++;
}
int endDecimal = startDecimal + 1;
while ((endDecimal < csq.length())
&& !Character.isWhitespace(csq.charAt(endDecimal))) {
endDecimal++;
}
Double decimal = new Double(csq.subSequence(startDecimal,
endDecimal).toString());
// cursor.setIndex(endDecimal + 1);
Unit unit = EBNFUnitFormat.getInstance().parse(csq, index);
return NumberQuantity.of(decimal.doubleValue(), unit);
}
public AbstractQuantity> parse(CharSequence csq)
throws ParserException {
return parse(csq, 0);
}
}
}