All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.units.StandardUnitFormat.jj Maven / Gradle / Ivy

Go to download

The ucar.units Java package is for decoding and encoding formatted unit specifications (e.g. "m/s"), converting numeric values between compatible units (e.g. between "m/s" and "knot"), and for performing arithmetic operations on units (e.g. dividing one unit by another, or raising a unit to a power).

The newest version!
/*
 * 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 - 2024 Weber Informatics LLC | Privacy Policy