[kepler-dev] [Ptolemy] Re: DateToken

Christopher Brooks cxh at eecs.berkeley.edu
Mon Jun 23 10:53:04 PDT 2014


The DateToken add, divide, modulo, multiply and subtract methods now all 
throw exceptions and have tests.

There are a number of reasons we have not had a DateToken that was 
convertable until now.

I fixed up the code so that if we call toString() on a DateToken and 
then parse the string, we get a DateToken equal to the first DateToken.  
The problem is that the toString() method of the java Date class does 
not include the value of the milliseconds.  Also, the default java Date 
parser does not parse the output from toString().  This is now fixed.

I also added isGreater() and isLessThan() methods because Date has 
after() and before() methods.

A larger problem has to do with type conversion.

See below for the previous value of the convert() method in BaseType for 
the DateType, which converts from LongToken and StringToken to DateToken.

> public Token convert(Token t) throws IllegalActionException {
>             if (t instanceof DateToken) {
>                 return t;
>             } else if (t instanceof LongToken) {
>                 return new DateToken(((LongToken) t).longValue());
>             } else if (t instanceof StringToken) {
>                 return new DateToken(((StringToken) t).stringValue());
>             }
>             throw new IllegalActionException(
>               Token.notSupportedIncomparableConversionMessage(t, "date"));
>             }

At first cut, this seems right, but I think ignores conversions from 
anything below LongToken, such as IntToken.  Also, not all StringTokens 
can be losslessly converted to DateTokens.  So, I added the 
DateToken.convert(), which is similar to ScalarToken.convert.

I started working on this, but ran in to various issues.    I added the 
following to TypeLattice so that we could convert from Long to Date:

> _basicLattice.addEdge(BaseType.UNKNOWN, BaseType.DATE);
>              _basicLattice.addEdge(BaseType.DATE, BaseType.STRING);
> +            // Allowing a conversion from STRING to a DATE would make 
> the lattice cyclic
> +            //_basicLattice.addEdge(BaseType.STRING, BaseType.DATE);
> +            _basicLattice.addEdge(BaseType.LONG, BaseType.DATE);

I added a convert() method to DateToken which seems to work well. I did 
hack in a check for StringToken which will try to instantiate a 
DateToken.  I'm not sure if that is right.

>     public static DateToken convert(Token token) throws 
> IllegalActionException {
>         if (token instanceof DateToken) {
>             return (DateToken) token;
>         }
>
>         if (token.isNil()) {
>             return DateToken.NIL;
>         }
>
>         int compare = TypeLattice.compare(BaseType.DATE, token);
>
>         if (compare == CPO.LOWER || compare == CPO.INCOMPARABLE) {
>             // This seems really wrong, but we want to be able to
>             // convert from a StringToken to a DateToken.
>             if (token instanceof StringToken) {
>                 return new DateToken(((StringToken) token).stringValue());
>             }
>             throw new IllegalActionException(
> notSupportedIncomparableConversionMessage(token, "date"));
>         }
>
>         compare = TypeLattice.compare(BaseType.LONG, token);
>
>         if (compare == CPO.SAME || compare == CPO.HIGHER) {
>             LongToken longToken = LongToken.convert(token);
>             DateToken result = new DateToken(longToken.longValue());
>             return result;
>         }
>         compare = TypeLattice.compare(BaseType.STRING, token);
>
>         if (compare == CPO.SAME || compare == CPO.HIGHER) {
>             StringToken stringToken = StringToken.convert(token);
>             DateToken result = new DateToken(stringToken.stringValue());
>             return result;
>         }
>
>         throw new 
> IllegalActionException(notSupportedConversionMessage(token,
>                 "date"));
>     }


Where I ran in to problems is adding code to allow the comparison of two 
DateTokens.

Right now, this test fails where I try to call DateToken.isGreaterThan() 
on a LongToken
> ==== $PTII/ptolemy/data/test/DateToken.tcl: DateToken-9.1 Test 
> isGreaterThan with other types
> ==== Contents of test case:
>
>     set long1 [java::new {ptolemy.data.LongToken long} 1]
>     set int1 [java::new {ptolemy.data.IntToken int} 1]
>     set short1 [java::new {ptolemy.data.ShortToken short} 1]
>
>     set t2 [java::new {ptolemy.data.DateToken long} 2]
>     list [[$t2 isGreaterThan $long1] toString]
>
>     #list [[$t2 isGreaterThan $int1] toString]
>     #[[$t2 isGreaterThan $short1] toString]
>
>
> ==== Test generated error:
>     while executing
> java.lang.ClassCastException: ptolemy.data.StringToken cannot be cast 
> to ptolemy.data.PartiallyOrderedToken
>         at ptolemy.data.DateToken.isGreaterThan(DateToken.java:226)
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at 
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

I'm not totally sure why the LongToken is getting converted to a 
StringToken?

In the code below TypeLattice.compare(getType(), (Token)rightArgument) 
is returning CPO.HIGHER and then the convert line is converting to a 
StringToken?

I'm lost.

line 226 is marked with --->
>    public final BooleanToken isGreaterThan(PartiallyOrderedToken 
> rightArgument)
>             throws IllegalActionException {
>         // Similar to the same method in ScalarToken.
>         int typeInfo = TypeLattice.compare(getType(), 
> (Token)rightArgument);
>
>         if (typeInfo == CPO.SAME) {
>             return ((DateToken)rightArgument)._doIsLessThan(this);
>         } else if (typeInfo == CPO.HIGHER) {
>             // This line is different from ScalarToken and causes 
> problems with StringTokens.
> ---->            PartiallyOrderedToken convertedArgument = 
> (PartiallyOrderedToken) getType().convert((Token)rightArgument);
>             try {
>                 return convertedArgument.isLessThan(this);
>             } catch (IllegalActionException ex) {
>         // If the type-specific operation fails, then create a
>         // better error message that has the types of the
>         // arguments that were passed in.
>         throw new IllegalActionException(null, ex, notSupportedMessage(
>                                 "isGreaterThan", (Token)this, 
> (Token)rightArgument));
>             }
>         } else if (typeInfo == CPO.LOWER) {
>             return rightArgument.isLessThan(this);
>         } else {
>             throw new 
> IllegalActionException(notSupportedIncomparableMessage(
>                             "isGreaterThan", (Token)this, 
> (Token)rightArgument));
>         }
>     }

At this point, I need to move on.

I've left these tests as failures for the time being, perhaps someone 
can look at this?


Bottom line: adding a type to the type lattice is not easy.

_Christopher

On 6/23/14 3:36 AM, Edward A. LEE wrote:
> Interesting questions about operations.
>
> Without thinking about it too much:
>
> - add doesn't make sense to me.  The sum of two dates is not a date.
> Sum of a date and a long (or an int) does, however. That is a date.
> Sum of a Date and a Time also makes sense to me... Though here, we
> have to assume units for Time (seconds?).
>
> - subtract of two Dates is also not a Date.  But it could be a London
> or a Time.
>
> Edward
>
> ---------
> Edward A. Lee
> EECS, UC Berkeley
> eal at eecs.berkeley.edu
>
>> On Jun 22, 2014, at 8:36 PM, Patricia Derler <patriciad at berkeley.edu> wrote:
>>
>> Christopher,
>>
>> Yes, I used the DateToken in Kepler and changed it. Thank you for going through the code and fixing some of it. I tried to make a minimal version of the DateToken work but forgot to implement several methods. Also, I think you are right, the type lattice needs to be changed.
>>
>> Patricia
>>
>>
>>> On 6/22/14, 7:39 PM, Christopher Brooks wrote:
>>> Hi Patricia,
>>> I saw that you checked in ptolemy.data.DateToken, which is a copy of kepler/util/src/org/kepler/date/DateToken.java so that we can use DateToken in Ptolemy (outside of Kepler).  The biggest difference between the two is that the Ptolemy version is convertible from LongToken.
>>>
>>> There was a FindBugs warning about ptolemy.dateToken.toString() returning null, so I fixed that.
>>>
>>> I also added support for nil DateTokens, so that we can support missing data.  The Kepler version probably does not support nil DateTokens.
>>>
>>> I also added tests for some of the above operations. Unfortunately, Ptjacl does not handle longs very well, so writing Tcl unit tests is tricky.
>>>
>>> The ptolemy.data.DateToken file was badly formatted, it could be that your Eclipse configuration is using tabs.  However, my guess is that the formatting issues were from the copy from Kepler.
>>>
>>> A few open issues:
>>>
>>> - In ptolemy.data.type.BaseType, we have:
>>>> /** The date data type. */
>>>>     public static class DateType extends BaseType {
>>>>         private DateType() {
>>>>             super(DateToken.class, "date");
>>>>         }
>>>>
>>>>         public Token convert(Token t) throws IllegalActionException {
>>>>             if (t instanceof DateToken) {
>>>>                 return t;
>>>>             } else if (t instanceof LongToken) {
>>>>                 return new DateToken(((LongToken) t).longValue());
>>>>             } else if (t instanceof StringToken) {
>>>>                 return new DateToken(((StringToken) t).stringValue());
>>>>             }
>>>>             throw new IllegalActionException(
>>>> Token.notSupportedIncomparableConversionMessage(t, "date"));
>>>>         }
>>>>
>>>>         public int getTypeHash() {
>>>>             return 15;
>>>>     }
>>> Isn't anything that is convertible to a LongToken losslessly acceptable here as well?
>>>
>>> Isn't it possible to losslessly convert from a DateToken to a LongToken?  Will this mess up the lattice?
>>>
>>> - In ptolemy.data.DateToken, you have an _isEqualTo() method and other methods, these are necessary because ptolemy.data.DateToken extends AbstractConvertibleToken.  However, there are a bunch of methods like _add(), _divide() etc. that have no method bodies.
>>>
>>> - Certain operations, such as add, are clearly doable if the Dates are converted to longs and then added and then converted back to DateTokens.  I think _add() and _subtract() could be possible
>>>
>>> - For _isCloseTo(token, double epsilon):     If we convert the two tokens to longs and the epsilon to a long, then this might make sense?   However, double is not losslessly convertible to long? Probably throw an IllegalActionException here.
>>>
>>> - I'm not sure it makes sense to divide a date by another date, but dividing a date by a long makes a certain amount of sense maybe?  I dunno.
>>>
>>> - Ditto with _multiply.
>>>
>>> - _modulo of two Dates might make sense but is confusing and should probably throw an exception.
>>>
>>> Does anyone have thoughts about the above?
>>>
>>> _Christopher
>> _______________________________________________
>> Ptolemy maillist  -  Ptolemy at chess.eecs.berkeley.edu
>> http://chess.eecs.berkeley.edu/ptolemy/listinfo/ptolemy


-- 
Christopher Brooks, PMP                       University of California
Academic Program Manager & Software Engineer  US Mail: 337 Cory Hall
CHESS/iCyPhy/Ptolemy/TerraSwarm               Berkeley, CA 94720-1774
cxh at eecs.berkeley.edu, 707.332.0670           (Office: 545Q Cory)



More information about the Kepler-dev mailing list