ucar.units.StandardUnitFormat.jj Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of netcdf Show documentation
Show all versions of netcdf Show documentation
The NetCDF-Java Library is a Java interface to NetCDF files,
as well as to many other types of scientific data formats.
/*
* 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;
}
}