001/* Construct date token by parsing all date elements (year, day, month, ...).
002
003   @Copyright (c) 2008-2015 The Regents of the University of California.
004   All rights reserved.
005
006   Permission is hereby granted, without written agreement and without
007   license or royalty fees, to use, copy, modify, and distribute this
008   software and its documentation for any purpose, provided that the
009   above copyright notice and the following two paragraphs appear in all
010   copies of this software.
011
012   IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013   FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014   ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015   THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016   SUCH DAMAGE.
017
018   THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021   PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022   CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023   ENHANCEMENTS, OR MODIFICATIONS.
024
025   PT_COPYRIGHT_VERSION_2
026   COPYRIGHTENDKEY
027
028 */
029package ptolemy.actor.lib;
030
031import java.util.Calendar;
032import java.util.TimeZone;
033
034import ptolemy.actor.TypedAtomicActor;
035import ptolemy.actor.TypedIOPort;
036import ptolemy.actor.parameters.PortParameter;
037import ptolemy.data.BooleanToken;
038import ptolemy.data.DateToken;
039import ptolemy.data.IntToken;
040import ptolemy.data.LongToken;
041import ptolemy.data.StringToken;
042import ptolemy.data.expr.Parameter;
043import ptolemy.data.expr.SingletonParameter;
044import ptolemy.data.expr.StringParameter;
045import ptolemy.data.type.BaseType;
046import ptolemy.kernel.CompositeEntity;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.NameDuplicationException;
049
050/**
051  Construct date token by parsing all date elements (year, day, month, ...).
052
053 @author Patricia Derler
054 @version $Id$
055 @since Ptolemy II 10.0
056 @Pt.ProposedRating Red (pd)
057 @Pt.AcceptedRating Red (pd)
058 */
059public class DateConstructor extends TypedAtomicActor {
060
061    /** Construct an actor with the given container and name.
062     *  @param container The container.
063     *  @param name The name of this actor.
064     *  @exception IllegalActionException If the actor cannot be contained
065     *   by the proposed container.
066     *  @exception NameDuplicationException If the container already has an
067     *   actor with this name.
068     */
069    public DateConstructor(CompositeEntity container, String name)
070            throws NameDuplicationException, IllegalActionException {
071        super(container, name);
072
073        output = new TypedIOPort(this, "output", false, true);
074        output.setTypeEquals(BaseType.DATE);
075
076        year = new PortParameter(this, "year", new IntToken(0));
077        new SingletonParameter(year.getPort(), "_showName")
078                .setToken(BooleanToken.TRUE);
079
080        month = new PortParameter(this, "month", new IntToken(0));
081        month.setTypeEquals(BaseType.INT);
082        new SingletonParameter(month.getPort(), "_showName")
083                .setToken(BooleanToken.TRUE);
084
085        day = new PortParameter(this, "day", new IntToken(1));
086        day.setTypeEquals(BaseType.INT);
087        new SingletonParameter(day.getPort(), "_showName")
088                .setToken(BooleanToken.TRUE);
089
090        hour = new PortParameter(this, "hour", new IntToken(0));
091        hour.setTypeEquals(BaseType.INT);
092        new SingletonParameter(hour.getPort(), "_showName")
093                .setToken(BooleanToken.TRUE);
094
095        minute = new PortParameter(this, "minute", new IntToken(0));
096        minute.setTypeEquals(BaseType.INT);
097        new SingletonParameter(minute.getPort(), "_showName")
098                .setToken(BooleanToken.TRUE);
099
100        second = new PortParameter(this, "second", new IntToken(0));
101        second.setTypeEquals(BaseType.INT);
102        new SingletonParameter(second.getPort(), "_showName")
103                .setToken(BooleanToken.TRUE);
104
105        millisecond = new PortParameter(this, "millisecond", new IntToken(0));
106        millisecond.setTypeEquals(BaseType.INT);
107        new SingletonParameter(millisecond.getPort(), "_showName")
108                .setToken(BooleanToken.TRUE);
109
110        microsecond = new PortParameter(this, "microsecond", new IntToken(0));
111        microsecond.setTypeEquals(BaseType.INT);
112        new SingletonParameter(microsecond.getPort(), "_showName")
113                .setToken(BooleanToken.TRUE);
114
115        nanosecond = new PortParameter(this, "nanosecond", new IntToken(0));
116        nanosecond.setTypeEquals(BaseType.INT);
117        new SingletonParameter(nanosecond.getPort(), "_showName")
118                .setToken(BooleanToken.TRUE);
119
120        timeZoneOffset = new PortParameter(this, "timeZoneOffset");
121        timeZoneOffset.setTypeEquals(BaseType.INT);
122        new SingletonParameter(timeZoneOffset.getPort(), "_showName")
123                .setToken(BooleanToken.TRUE);
124
125        useTimeInMillis = new Parameter(this, "useTimeInMillis",
126                new BooleanToken(false));
127        useTimeInMillis.setTypeEquals(BaseType.BOOLEAN);
128
129        timeInMillis = new PortParameter(this, "timeInMillis", new IntToken(0));
130        timeInMillis.setTypeEquals(BaseType.LONG);
131        new SingletonParameter(timeInMillis.getPort(), "_showName")
132                .setToken(BooleanToken.TRUE);
133
134        precision = new StringParameter(this, "precision");
135        precision.addChoice("second");
136        precision.addChoice("millisecond");
137        precision.addChoice("microsecond");
138        precision.addChoice("nanosecond");
139        precision.setExpression("millisecond");
140    }
141
142    /** The output for the constructed date.
143     */
144    public TypedIOPort output;
145
146    /** The year.
147     */
148    public PortParameter year;
149
150    /** The month.
151     */
152    public PortParameter month;
153
154    /** The day of the month.
155     */
156    public PortParameter day;
157
158    /** The hour of the day.
159     */
160    public PortParameter hour;
161
162    /** The minutes.
163     */
164    public PortParameter minute;
165
166    /** The seconds.
167     */
168    public PortParameter second;
169
170    /** The milliseconds.
171     */
172    public PortParameter millisecond;
173
174    /** The microseconds.
175     */
176    public PortParameter microsecond;
177
178    /** The nanoseconds.
179     */
180    public PortParameter nanosecond;
181
182    /** The time zone offset.
183     */
184    public PortParameter timeZoneOffset;
185
186    /** The time as a long value representing the milliseconds since
187     *  January 1, 1970.
188     */
189    public PortParameter timeInMillis;
190
191    /** Use the time in milliseconds. The default value is false,
192     *  which means that the timeAsLongValue will be set according the
193     *  year, month, day etc and adjusted according to the precision.
194     *  If useTimeInMillis is true, then the value of timeAsLongValue
195     *  will be the time in milliseconds.
196     */
197    public Parameter useTimeInMillis;
198
199    /** The precision of the date. The precision defaults to
200     *  milliseconds.
201     */
202    public Parameter precision;
203
204    ///////////////////////////////////////////////////////////////////
205    ////                         public methods                    ////
206
207    /** Construct a date token with all tokens present. If a token
208     *  for the long value is present, use this token and time zone
209     *  as well as precision. Otherwise use tokens on other inputs to
210     *  create ports.
211     *  @exception IllegalActionException If thrown by the superclass,
212     *  if there is no director, ifthere is a problem reading a
213     *  parameter, or if there is a problem sending the date token.
214     */
215    @Override
216    public void fire() throws IllegalActionException {
217        super.fire();
218        DateToken dateToken = null;
219        int datePrecision = DateToken.PRECISION_MILLISECOND;
220
221        // Parse the value of the precision parameter.  If the value
222        // is non-standard, default to milliseconds.
223        // FIXME:This would be better off using final strings
224        // in the constructor and here so as to avoid problems.
225        String precisionValue = ((StringToken) precision.getToken())
226                .stringValue();
227        if (precisionValue.equals("second")) {
228            datePrecision = DateToken.PRECISION_SECOND;
229        } else if (precisionValue.equals("millisecond")) {
230            datePrecision = DateToken.PRECISION_MILLISECOND;
231        } else if (precisionValue.equals("microsecond")) {
232            datePrecision = DateToken.PRECISION_MICROSECOND;
233        } else if (precisionValue.equals("nanosecond")) {
234            datePrecision = DateToken.PRECISION_NANOSECOND;
235        } else {
236            datePrecision = DateToken.PRECISION_MILLISECOND;
237        }
238        Integer timeZoneValue = _getIntValue(timeZoneOffset);
239        TimeZone timeZone = TimeZone.getDefault();
240        if (timeZoneValue != null) {
241            // If offset is negative, it will already have a - sign
242            // Just need to add + sign for positive offsets
243            timeZone = TimeZone
244                    .getTimeZone("GMT" + (timeZoneValue < 0 ? "" : "+")
245                            + String.format("%04d", timeZoneValue));
246        }
247
248        long timeAsLongValue = _getLongValue(timeInMillis);
249        int microsecondValue = _getIntValue(microsecond);
250        int nanosecondValue = _getIntValue(nanosecond);
251
252        if (!((BooleanToken) useTimeInMillis.getToken()).booleanValue()) {
253            int yearValue = _getIntValue(year);
254            int monthValue = _getIntValue(month);
255            int dayValue = _getIntValue(day);
256            int hourValue = _getIntValue(hour);
257            int minuteValue = _getIntValue(minute);
258            int secondValue = _getIntValue(second);
259            int millisecondValue = _getIntValue(millisecond);
260
261            Calendar c = Calendar.getInstance(timeZone);
262            c.set(Calendar.YEAR, yearValue);
263            c.set(Calendar.MONTH, monthValue);
264            c.set(Calendar.DAY_OF_MONTH, dayValue);
265            c.set(Calendar.HOUR_OF_DAY, hourValue);
266            c.set(Calendar.MINUTE, minuteValue);
267            c.set(Calendar.SECOND, secondValue);
268            c.set(Calendar.MILLISECOND, millisecondValue);
269            timeAsLongValue = c.getTimeInMillis();
270
271            // FIXME: the precision should be an enum and this should be
272            // a switch.
273            if (datePrecision == DateToken.PRECISION_SECOND) {
274                timeAsLongValue = timeAsLongValue / 1000;
275            } else if (datePrecision == DateToken.PRECISION_MICROSECOND) {
276                timeAsLongValue = timeAsLongValue * 1000;
277            } else if (datePrecision == DateToken.PRECISION_NANOSECOND) {
278                timeAsLongValue = timeAsLongValue * 1000 * 1000;
279            }
280        }
281        dateToken = new DateToken(timeAsLongValue, datePrecision, timeZone);
282        dateToken.addMicroseconds(microsecondValue);
283        dateToken.addNanoseconds(nanosecondValue);
284
285        output.send(0, dateToken);
286    }
287
288    ///////////////////////////////////////////////////////////////////
289    ////                         private methods                   ////
290
291    private Integer _getIntValue(PortParameter portParameter)
292            throws IllegalActionException {
293        Integer value = null;
294        if (portParameter.getToken() != null) {
295            value = ((IntToken) portParameter.getToken()).intValue();
296        }
297        if (portParameter.getPort().connectedPortList().size() > 0
298                && portParameter.getPort().hasToken(0)) {
299            value = ((IntToken) portParameter.getPort().get(0)).intValue();
300        }
301        // // Coverity Scan reports that value could be null.
302        // if (value == null) {
303        //     return Integer.valueOf(0);
304        // }
305        return value;
306    }
307
308    private Long _getLongValue(PortParameter portParameter)
309            throws IllegalActionException {
310        Long value = null;
311        if (portParameter.getToken() != null) {
312            value = ((LongToken) portParameter.getToken()).longValue();
313        }
314        if (portParameter.getPort().connectedPortList().size() > 0
315                && portParameter.getPort().hasToken(0)) {
316            value = ((LongToken) portParameter.getPort().get(0)).longValue();
317        }
318        // // Coverity Scan reports that value could be null.
319        // if (value == null) {
320        //    return Long.valueOf(0);
321        // }
322        return value;
323    }
324
325}