ucar.units.StandardUnitFormat.jj Maven / Gradle / Ivy
/*
* Copyright 1999, 2009 University Corporation for Atmospheric Research/Unidata
* See file LICENSE for legal details.
*/
options
{
STATIC = false;
// DEBUG_PARSER = true;
// DEBUG_TOKEN_MANAGER = true;
// LOOKAHEAD=2;
// FORCE_LA_CHECK=true;
}
PARSER_BEGIN(StandardUnitFormat)
package ucar.units;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* Standard formatter/parser for unit specifications.
*
* Instances of this class are thread-compatible but not thread-safe.
*
* @author Steven R. Emmerson
*/
public final class
StandardUnitFormat
extends UnitFormatImpl
{
private static final long serialVersionUID = 2L;
/**
* The singleton instance of this class.
* @serial
*/
private static StandardUnitFormat _instance;
/**
* The date formatter.
* @serial
*/
private static final SimpleDateFormat dateFormat;
/**
* The Comparator for ordering base units for printing. Orders
* Factor-s by decreasing exponent (major) and lexically (minor).
* @serial
*/
private static final Comparator factorComparator =
new Comparator()
{
public int compare(Factor f1, Factor f2)
{
int comp = f2.getExponent() - f1.getExponent();
if (comp == 0)
comp = f1.getID().compareTo(f2.getID());
return comp;
}
};
static
{
dateFormat =
(SimpleDateFormat)DateFormat.getDateInstance(
DateFormat.SHORT, Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
dateFormat.applyPattern(" '@' yyyy-MM-dd HH:mm:ss.SSS 'UTC'");
}
/**
* Constructs from nothing.
*/
private
StandardUnitFormat()
{
this(new StringReader(""));
}
/**
* Returns an instance of this class.
* @return An instance of this class.
*/
public static StandardUnitFormat
instance()
{
if (_instance == null)
{
synchronized(StandardUnitFormat.class)
{
if (_instance == null)
_instance = new StandardUnitFormat();
}
}
return _instance;
}
/**
* Indicates if a unit is a time unit.
*
* @param unit The unit in question.
* @return {@code true} if and only if {@code unit} is a time unit.
* @Throws UnitSystemException if the unit system can't be initialized.
*/
private static boolean isTimeUnit(final Unit unit) throws UnitSystemException {
return unit.isCompatible(UnitSystemManager.instance().getBaseUnit(
BaseQuantity.TIME));
}
/**
* Decodes a unit specification. An unrecognized unit is made into
* an UnknownUnit.
* @param spec The unit specification to be decoded.
* @param unitDB The unit database to use.
* @return The unit corresponding to the specification.
* @throws UnitParseException The unit specification syntax is
* invalid.
* @throws SpecificationException Something's wrong with the
* specification.
* @throws UnitDBException Something's wrong with the unit
* database.
* @throws PrefixDBException Something's wrong with the unit prefix
* database.
* @throws UnitSystemException Something's wrong with the underlying
system of units.
*/
public Unit
parse(String spec, UnitDB unitDB)
throws UnitParseException,
SpecificationException,
UnitDBException,
PrefixDBException,
UnitSystemException
{
ReInit(new StringReader(spec.trim()));
try
{
Unit unit = unitSpec(unitDB);
return unit;
}
catch (TokenMgrError e)
{
throw new UnitParseException(spec, e);
}
catch (ParseException e)
{
throw new UnitParseException(spec, e);
}
catch (OperationException e)
{
throw new SpecificationException(spec, e);
}
}
/**
* Formats a Factor.
* @param factor The factor to be formatted.
* @param buf The buffer to append to.
* @return The appended-to buffer.
*/
public StringBuffer
format(Factor factor, StringBuffer buf)
{
return buf.append(factor.toString());
}
/**
* Formats a unit. The symbol or name will be used if available;
* otherwise, a specification in terms of underlying units will be
* returned.
* @param unit The unit to be formatted.
* @param buf The buffer to append to.
* @return The appended-to buffer.
* @throws UnitClassException The class of the unit is unknown.
*/
public StringBuffer
format(Unit unit, StringBuffer buf)
throws UnitClassException
{
return format(unit, buf, true);
}
/**
* Formats a unit in the underlying system of units.
* @param unit The unit to be formatted.
* @param buf The buffer to append to.
* @return The appended-to buffer.
* @throws UnitClassException The class of the unit is unknown.
*/
public StringBuffer
longFormat(Unit unit, StringBuffer buf)
throws UnitClassException
{
return format(unit, buf, false);
}
/**
* Formats a unit.
* @param unit The unit to be formatted.
* @param buf The buffer to append to.
* @param normalize Whether or not to reduce the unit.
* @return The appended-to buffer.
* @throws UnitClassException The class of the unit is unknown.
*/
private StringBuffer
format(Unit unit, StringBuffer buf, boolean normalize)
throws UnitClassException
{
boolean done = false;
if (!normalize)
{
String id = unit.getSymbol();
if (id == null)
id = unit.getName();
if (id != null)
{
buf.append(id.replace(' ', '_'));
done = true;
}
}
if (!done)
{
if (unit instanceof BaseUnit)
format((BaseUnit)unit, buf);
else
if (unit instanceof DerivedUnit)
format((DerivedUnit)unit, buf);
else
if (unit instanceof ScaledUnit)
format((ScaledUnit)unit, buf, normalize);
else
if (unit instanceof OffsetUnit)
format((OffsetUnit)unit, buf, normalize);
else
if (unit instanceof TimeScaleUnit)
format((TimeScaleUnit)unit, buf, normalize);
else
throw new UnitClassException(unit);
}
return buf;
}
private StringBuffer
format(BaseUnit baseUnit, StringBuffer buf)
{
return buf.append(baseUnit.getSymbol());
}
private StringBuffer
format(DerivedUnit unit, StringBuffer buf)
{
Factor[] factors = unit.getDimension().getFactors();
Arrays.sort(factors, factorComparator);
for (int i = 0; i < factors.length; i++)
format(factors[i], buf).append('.');
if (factors.length != 0)
buf.setLength(buf.length()-1);
return buf;
}
private StringBuffer
format(ScaledUnit unit, StringBuffer buf, boolean normalize)
throws UnitClassException
{
double scale = unit.getScale();
if (scale != 0.0)
{
if (scale == 1)
{
format(unit.getUnit(), buf, normalize);
}
else
{
buf.append(scale).append(' ');
int start = buf.length();
format(unit.getUnit(), buf, normalize);
if (start == buf.length())
buf.setLength(start-1);
}
}
return buf;
}
private StringBuffer
format(OffsetUnit unit, StringBuffer buf, boolean normalize)
throws UnitClassException
{
double offset = unit.getOffset();
if (offset == 0.0)
{
format(unit.getUnit(), buf, normalize);
}
else
{
int start = buf.length();
format(unit.getUnit(), buf, normalize);
return (isBlackSpace(buf, start)
? buf
: buf.insert(start, '(').append(')')).
append(" @ ").append(offset);
}
return buf;
}
private static boolean
isBlackSpace(StringBuffer buf, int start)
{
return buf.substring(start).indexOf(' ') == -1;
}
private StringBuffer
format(TimeScaleUnit unit, StringBuffer buf, boolean normalize)
throws UnitClassException
{
return format(unit.getUnit(), buf, normalize).
append(dateFormat.format(unit.getOrigin()));
}
/**
* Gets a unit from a unit database.
*/
private static Unit
getUnit(UnitDB unitDB, String string)
throws UnitDBAccessException
{
return unitDB.get(string);
}
/**
* Gets a prefix from the prefix database.
*/
private static Prefix
getPrefix(String string)
throws PrefixDBException
{
PrefixDB prefixDB = PrefixDBManager.instance();
Prefix prefix = prefixDB.getPrefixByName(string);
if (prefix == null)
prefix = prefixDB.getPrefixBySymbol(string);
return prefix;
}
private static void myAssert(StandardUnitFormat parser, final String spec,
final Unit unit) throws NoSuchUnitException,
UnitParseException, SpecificationException, UnitDBException,
PrefixDBException, UnitSystemException
{
if (!parser.parse(spec).equals(unit)) {
throw new AssertionError(spec + " != " + unit);
}
System.out.println(spec + " -> " + unit);
}
/**
* Test this class.
*/
public static void main(String[] args)
throws Exception
{
StandardUnitFormat parser =
StandardUnitFormat.instance();
final Unit m = parser.parse("m");
final Unit s = parser.parse("s");
final Unit epoch = parser.parse("s @ 1970-01-01 00:00:00 UTC");
myAssert(parser, "m m", m.multiplyBy(m));
myAssert(parser, "m.m", m.multiplyBy(m));
myAssert(parser, "(m)(m)", m.multiplyBy(m));
myAssert(parser, "m/s/s", m.divideBy(s).divideBy(s));
myAssert(parser, "m2", m.raiseTo(2));
myAssert(parser, "m2.s", m.raiseTo(2).multiplyBy(s));
myAssert(parser, "m2/s", m.raiseTo(2).divideBy(s));
myAssert(parser, "m^2/s", m.raiseTo(2).divideBy(s));
myAssert(parser, "m s @ 5", m.multiplyBy(s).shiftTo(5.0));
myAssert(parser, "m2 s @ 5", m.raiseTo(2).multiplyBy(s).shiftTo(5));
myAssert(parser, "m2 s-1 @ 5", m.raiseTo(2).multiplyBy(s.raiseTo(-1))
.shiftTo(5));
myAssert(parser, "m s from 5", m.multiplyBy(s).shiftTo(5));
myAssert(parser, "s@19700101", epoch);
myAssert(parser, "s@19700101T000000", epoch);
myAssert(parser, "[email protected]", epoch);
myAssert(parser, "s @ 1970-01-01T00:00:00.00", epoch);
myAssert(parser, "s @ 1970-01-01 00:00:00.00", epoch);
myAssert(parser, "s @ 1970-01-01 00:00:00.00 +0", epoch);
myAssert(parser, "s @ 1970-01-01T00:00:00.00 -12", epoch
.shiftTo(new Date(12 * 60 * 60 * 1000)));
if (!parser.parse("days since 2009-06-14 04:00:00").equals(
parser.parse("days since 2009-06-14 04:00:00 +00:00"))) {
throw new AssertionError();
}
myAssert(parser, "lg(re: 1)", DerivedUnitImpl.DIMENSIONLESS.log(10));
myAssert(parser, "0.1 lg(re 1 mm)", m.multiplyBy(1e-3).log(10)
.multiplyBy(0.1));
myAssert(parser, "m", m);
myAssert(parser, "2 m s", m.multiplyBy(s).multiplyBy(2));
myAssert(parser, "3.14 m.s", m.multiplyBy(s).multiplyBy(3.14));
myAssert(parser, "1e9 (m)", m.multiplyBy(1e9));
myAssert(parser, "(m s)2", m.multiplyBy(s).raiseTo(2));
myAssert(parser, "m2.s-1", m.raiseTo(2).divideBy(s));
myAssert(parser, "m2 s^-1", m.raiseTo(2).divideBy(s));
myAssert(parser, "(m/s)2", m.divideBy(s).raiseTo(2));
myAssert(parser, "m2/s-1", m.raiseTo(2).divideBy(s.raiseTo(-1)));
myAssert(parser, "m2/s^-1", m.raiseTo(2).divideBy(s.raiseTo(-1)));
myAssert(parser, ".5 m/(.25 s)2", m.multiplyBy(.5).divideBy(
s.multiplyBy(.25).raiseTo(2)));
myAssert(parser, "m.m-1.m", m.multiplyBy(m.raiseTo(-1)).multiplyBy(m));
myAssert(parser, "2.0 m 1/2 s-1*(m/s^1)^-1 (1e9 m-1)(1e9 s-1)-1.m/s", m
.multiplyBy(2).multiplyBy(1. / 2.).multiplyBy(
s.raiseTo(-1)).multiplyBy(
m.divideBy(s.raiseTo(1)).raiseTo(-1))
.multiplyBy(m.raiseTo(-1).multiplyBy(1e9)).multiplyBy(
s.raiseTo(-1).multiplyBy(1e9).raiseTo(-1))
.multiplyBy(m).divideBy(s));
myAssert(parser, "m/km", m.divideBy(m.multiplyBy(1e3)));
LineNumberReader lineInput = new LineNumberReader(
new InputStreamReader(System.in));
for (;;)
{
System.out.print("Enter a unit specification or ^D to quit: ");
String spec = lineInput.readLine();
if (spec == null)
break;
try
{
System.out.println(parser.parse(spec.trim()));
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
System.out.println("");
}
}
PARSER_END(StandardUnitFormat)
TOKEN [IGNORE_CASE] :
{
< SP: ([" ","\t","\n","\r"])+ >
| < PLUS: "+" >
| < MINUS: "-" >
| < COLON: ":" >
| < UINT: (["0"-"9"])+ >
| <#SIGN: | >
| <#LETTER: ["a"-"z","_"] >
| < LPAREN: "(" >
| < RPAREN: ")" >
| < REAL_EXP: "e" ()? >
| < RAISE: "^" >
| < PERIOD: "." >
| < STAR: "*" >
| < DIVIDE: "/" | "per" >
| < SHIFT: ()? "@" ()? | ("since" | "from") >
| < SYMBOL: ["'","\"","%"] >
| < T: "t" >
| < NAME: ()+ ( ()+)? >
| < LB: "lb(re" (":")? ()? >
| < LN: "ln(re" (":")? ()? >
| < LG: "lg(re" (":")? ()? >
}
Unit unitSpec(UnitDB unitDB)
throws
OperationException,
UnitSystemException,
PrefixDBException,
UnitDBException :
{
Unit unit = DerivedUnitImpl.DIMENSIONLESS;
}
{
[
unit=shiftExpr(unitDB)
]
{
return unit;
}
}
Unit shiftExpr(UnitDB unitDB)
throws
OperationException,
UnitSystemException,
PrefixDBException,
UnitDBException :
{
Unit unit;
Date timestamp;
double origin;
}
{
unit=productExpr(unitDB)
[
(
LOOKAHEAD({isTimeUnit(unit)})
timestamp=timeOriginExpr()
{
unit = unit.shiftTo(timestamp);
}
|
origin=number()
{
unit = unit.shiftTo(origin);
}
)
]
{
return unit;
}
}
Unit productExpr(UnitDB unitDB)
throws
OperationException,
UnitSystemException,
PrefixDBException,
UnitDBException :
{
Unit unit;
Unit unit2;
}
{
unit=powerExpr(unitDB)
(
unit2 = powerExpr(unitDB)
{
unit = unit.divideBy(unit2);
}
|
LOOKAHEAD(2)
[ | | ]
unit2=powerExpr(unitDB)
{
unit = unit.multiplyBy(unit2);
}
)*
{
return unit;
}
}
Unit powerExpr(UnitDB unitDB)
throws
OperationException,
UnitSystemException,
PrefixDBException,
UnitDBException :
{
Unit unit;
int exponent;
}
{
unit=basicExpr(unitDB)
[
[ ]
exponent=integer()
{
unit = unit.raiseTo(exponent);
}
]
{
return unit;
}
}
Unit basicExpr(UnitDB unitDB)
throws
OperationException,
UnitSystemException,
PrefixDBException,
UnitDBException :
{
Unit unit;
double number;
}
{
(
number = number()
{
unit = DerivedUnitImpl.DIMENSIONLESS.multiplyBy(number);
}
|
unit=unitIdentifier(unitDB)
|
unit=logExpr(unitDB)
|
[] unit=shiftExpr(unitDB) []
)
{
return unit;
}
}
Unit logExpr(UnitDB unitDB)
throws
OperationException,
UnitSystemException,
PrefixDBException,
UnitDBException :
{
double base;
Unit ref;
}
{
(
{base = 2;}
|
{base = Math.E;}
|
{base = 10;}
)
ref = productExpr(unitDB) []
{
return ref.log(base);
}
}
double number() :
{
double number;
}
{
(
LOOKAHEAD(real())
number = real()
|
number = integer()
)
{
return number;
}
}
double real() :
{
int sign = 1;
double tenFactor = 1;
double udecimal;
}
{
[sign = sign()]
(
LOOKAHEAD(2)
udecimal = unsignedDecimal()
[ tenFactor = tenFactor() ]
|
udecimal = unsignedInteger()
tenFactor = tenFactor()
)
{
return sign * udecimal * tenFactor;
}
}
int sign() :
{}
{
{ return 1; }
|
{ return -1; }
}
double unsignedDecimal() :
{
int integer = 0;
Token token;
double fraction = 0;
}
{
(
LOOKAHEAD(3)
[integer=unsignedInteger()]
token =
{
fraction = Double.valueOf("." + token.image);
}
|
integer=unsignedInteger()
)
{
return integer + fraction;
}
}
double tenFactor() :
{
Token token;
}
{
token =
{
return Double.valueOf("1" + token.image);
}
}
int integer() :
{
int magnitude;
int sign = 1;
}
{
[ sign = sign() ]
magnitude = unsignedInteger()
{
return sign * magnitude;
}
}
int unsignedInteger() :
{
Token token;
}
{
token=
{
return Integer.valueOf(token.image);
}
}
Unit unitIdentifier(UnitDB unitDB)
throws UnitDBException, UnitSystemException, PrefixDBException, OperationException:
{
Token token;
Unit unit;
}
{
(
token=
|
token=
|
token=
)
{
String string = token.image;
double scale = 1;
for (unit = getUnit(unitDB, string);
unit == null;
unit = getUnit(unitDB, string))
{
Prefix prefix = getPrefix(string);
if (prefix == null)
{
try
{
// System.err.println("Unknown unit: \"" + string + '"');
unit = UnknownUnit.create(string);
break;
}
catch (NameException e)
{} // shouldn't happen
}
scale *= prefix.getValue();
string = string.substring(prefix.length());
}
unit = unit.multiplyBy(scale);
}
{
return unit;
}
}
/*
* See for a discussion of the
* relevant timestamp format or lookup "ISO 8601".
*/
Date timeOriginExpr() :
{
Calendar calendar;
}
{
calendar = dateExpr()
[
LOOKAHEAD(2)
( | )
clockExpr(calendar)
[
LOOKAHEAD(2)
[]
zoneExpr(calendar)
]
]
{
return calendar.getTime();
}
}
Calendar dateExpr() :
{
int sign = 1;
int year;
int month = 1;
int day = 1;
boolean packed = true;
}
{
[sign = sign() ]
year = unsignedInteger()
[
month = unsignedInteger()
{
packed = false;
}
[
day = unsignedInteger()
]
]
{
if (packed) {
if (year >= 10000101) {
day = year % 100;
year /= 100;
}
if (year >= 100001) {
month = year % 100;
year /= 100;
}
if (sign < 0)
year = -year;
}
if (month < 1 || month > 12)
throw new ParseException("invalid month in timestamp");
if (day < 1 || day > 31)
throw new ParseException("invalid day in timestamp");
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.clear();
calendar.set(year, month-1, day);
return calendar;
}
}
Calendar clockExpr(Calendar calendar) :
{
double hour;
int minute = 0;
double seconds = 0;
boolean packed = true;
}
{
(
LOOKAHEAD(unsignedDecimal())
hour = unsignedDecimal()
|
hour = unsignedInteger()
)
[
minute = unsignedInteger()
{
packed = false;
}
[
(
LOOKAHEAD(unsignedDecimal())
seconds = unsignedDecimal()
|
seconds = unsignedInteger()
)
]
]
{
if (packed) {
if (hour >= 100000) {
seconds = hour % 100;
hour /= 100;
}
if (hour >= 1000) {
minute = (int)(hour % 100);
hour /= 100;
}
}
if (hour < 0 || hour > 23)
throw new ParseException("invalid hour in timestamp");
if (minute < 0 || minute > 59)
throw new ParseException("invalid minute in timestamp");
if (seconds < 0 || seconds > 61)
throw new ParseException("invalid seconds in timestamp");
calendar.set(Calendar.HOUR_OF_DAY, (int)Math.round(hour));
calendar.set(Calendar.MINUTE, minute);
int s = (int)seconds;
calendar.set(Calendar.SECOND, s);
int ms = (int)((seconds - s) * 1000);
calendar.set(Calendar.MILLISECOND, ms);
return calendar;
}
}
Calendar zoneExpr(Calendar calendar) :
{
int sign = 1;
int zoneHour;
int zoneMinute = 0;
Token token;
TimeZone timeZone;
}
{
(
[sign=sign()]
zoneHour=unsignedInteger()
[[] zoneMinute=unsignedInteger()]
{
if (zoneHour >= 100)
{
zoneMinute += zoneHour % 100;
zoneHour /= 100;
}
if (zoneHour > 23 || zoneMinute > 59)
{
throw new ParseException("invalid time-zone in timestamp");
}
timeZone = TimeZone.getTimeZone("UTC");
timeZone.setRawOffset(sign*(zoneHour*60 + zoneMinute)*60*1000);
}
|
token =
{
timeZone = TimeZone.getTimeZone(token.image);
}
)
{
calendar.setTimeZone(timeZone);
return calendar;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy