001/* 002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences. 003 * Copyright (C) 2007 - JScience (http://jscience.org/) 004 * All rights reserved. 005 * 006 * Permission to use, copy, modify, and distribute this software is 007 * freely granted, provided that this notice is preserved. 008 */ 009package javax.measure; 010 011import java.math.BigDecimal; 012import java.text.FieldPosition; 013import java.text.Format; 014import java.text.NumberFormat; 015import java.text.ParseException; 016import java.text.ParsePosition; 017 018import javax.measure.unit.CompoundUnit; 019import javax.measure.unit.Unit; 020import javax.measure.unit.UnitFormat; 021 022/** 023 * <p> This class provides the interface for formatting and parsing {@link 024 * Measure measures}.</p> 025 * 026 * <p> As a minimum, instances of this class should be able to parse/format 027 * measure using {@link CompoundUnit}. </p> 028 * 029 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 030 * @version 4.2, August 26, 2007 031 */ 032public abstract class MeasureFormat extends Format { 033 034 /** 035 * Returns the measure format for the default locale. 036 * 037 * @return <code>getInstance(Number.getInstance(), Unit.getInstance())</code> 038 */ 039 public static MeasureFormat getInstance() { 040 return DEFAULT; 041 } 042 043 static final NumberUnit DEFAULT = new NumberUnit(NumberFormat 044 .getInstance(), UnitFormat.getInstance()); 045 046 /** 047 * Returns the measure format using the specified number format and 048 * unit format (the number and unit are separated by a space). 049 * 050 * @param numberFormat the number format. 051 * @param unitFormat the unit format. 052 * @return the corresponding format. 053 */ 054 public static MeasureFormat getInstance(NumberFormat numberFormat, 055 UnitFormat unitFormat) { 056 return new NumberUnit(numberFormat, unitFormat); 057 } 058 059 // Holds default implementation. 060 static final class NumberUnit extends MeasureFormat { 061 private final NumberFormat _numberFormat; 062 063 private final UnitFormat _unitFormat; 064 065 private NumberUnit(NumberFormat numberFormat, UnitFormat unitFormat) { 066 _numberFormat = numberFormat; 067 _unitFormat = unitFormat; 068 } 069 070 @Override 071 public StringBuffer format(Object obj, StringBuffer toAppendTo, 072 FieldPosition pos) { 073 Measure<?, ?> measure = (Measure<?, ?>) obj; 074 Object value = measure.getValue(); 075 Unit<?> unit = measure.getUnit(); 076 if (value instanceof Number) { 077 if (unit instanceof CompoundUnit) 078 return formatCompound(((Number) value).doubleValue(), 079 (CompoundUnit<?>) unit, toAppendTo, pos); 080 _numberFormat.format(value, toAppendTo, pos); 081 } else { 082 toAppendTo.append(value); 083 } 084 if (!measure.getUnit().equals(Unit.ONE)) { 085 toAppendTo.append(' '); 086 _unitFormat.format(unit, toAppendTo, pos); 087 } 088 return toAppendTo; 089 } 090 091 // Measure using Compound unit have no separators in their representation. 092 StringBuffer formatCompound(double value, Unit<?> unit, 093 StringBuffer toAppendTo, FieldPosition pos) { 094 if (!(unit instanceof CompoundUnit)) { 095 toAppendTo.append((long) value); 096 return _unitFormat.format(unit, toAppendTo, pos); 097 } 098 Unit<?> high = ((CompoundUnit<?>) unit).getHigher(); 099 Unit<?> low = ((CompoundUnit<?>) unit).getLower(); // The unit in which the value is stated. 100 long highValue = (long) low.getConverterTo(high).convert(value); 101 double lowValue = value 102 - high.getConverterTo(low).convert(highValue); 103 formatCompound(highValue, high, toAppendTo, pos); 104 formatCompound(lowValue, low, toAppendTo, pos); 105 return toAppendTo; 106 } 107 108 @Override 109 public Object parseObject(String source, ParsePosition pos) { 110 int start = pos.getIndex(); 111 try { 112 int i = start; 113 Number value = _numberFormat.parse(source, pos); 114 if (i == pos.getIndex()) 115 return null; // Cannot parse. 116 i = pos.getIndex(); 117 if (i >= source.length()) 118 return measureOf(value, Unit.ONE); // No unit. 119 boolean isCompound = !Character.isWhitespace(source.charAt(i)); 120 if (isCompound) 121 return parseCompound(value, source, pos); 122 if (++i >= source.length()) 123 return measureOf(value, Unit.ONE); // No unit. 124 pos.setIndex(i); // Skips separator. 125 Unit<?> unit = _unitFormat.parseProductUnit(source, pos); 126 return measureOf(value, unit); 127 } catch (ParseException e) { 128 pos.setIndex(start); 129 pos.setErrorIndex(e.getErrorOffset()); 130 return null; 131 } 132 } 133 134 @SuppressWarnings("unchecked") 135 private Object parseCompound(Number highValue, String source, 136 ParsePosition pos) throws ParseException { 137 Unit high = _unitFormat.parseSingleUnit(source, pos); 138 int i = pos.getIndex(); 139 if (i >= source.length() 140 || Character.isWhitespace(source.charAt(i))) 141 return measureOf(highValue, high); 142 Measure lowMeasure = (Measure) parseObject(source, pos); 143 Unit unit = lowMeasure.getUnit(); 144 long l = lowMeasure.longValue(unit) 145 + (long) high.getConverterTo(unit).convert( 146 highValue.longValue()); 147 return Measure.valueOf(l, unit); 148 } 149 150 @SuppressWarnings("unchecked") 151 private static Measure measureOf(Number value, Unit unit) { 152 if (value instanceof Double) { 153 return Measure.valueOf(value.doubleValue(), unit); 154 } else if (value instanceof Long) { 155 return Measure.valueOf(value.longValue(), unit); 156 } else if (value instanceof Float) { 157 return Measure.valueOf(value.floatValue(), unit); 158 } else if (value instanceof Integer) { 159 return Measure.valueOf(value.intValue(), unit); 160 } else if (value instanceof BigDecimal) { 161 return DecimalMeasure.valueOf((BigDecimal) value, unit); 162 } else { 163 return Measure.valueOf(value.doubleValue(), unit); 164 } 165 } 166 167 private static final long serialVersionUID = 1L; 168 } 169}