001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.scxml2.env;
018
019import java.util.Timer;
020import java.util.TimerTask;
021
022import org.apache.commons.scxml2.model.ModelException;
023
024/**
025 * A SCXML document driven stop watch.
026 *
027 * Using SCXML makes the StopWatch class simplistic; you are neither
028 * managing the stopwatch "lifecycle" nor coding any "transitions",
029 * that information is pulled in straight from the behavioral model
030 * of the stop watch, which is encapsulated in the SCXML document
031 * the constructor points to (which in turn may be generated straight
032 * from the UML model).
033 */
034public class StopWatch extends AbstractStateMachine {
035
036    /** The events for the stop watch. */
037    public static final String EVENT_START = "watch.start",
038        EVENT_STOP = "watch.stop", EVENT_SPLIT = "watch.split",
039        EVENT_UNSPLIT = "watch.unsplit", EVENT_RESET = "watch.reset";
040
041    /** The fragments of the elapsed time. */
042    private int hr, min, sec, fract;
043    /** The fragments of the display time. */
044    private int dhr, dmin, dsec, dfract;
045    /** The stopwatch "split" (display freeze). */
046    private boolean split;
047    /** The Timer to keep time. */
048    private Timer timer;
049    /** The display decorations. */
050    private static final String DELIM = ":", DOT = ".", EMPTY = "", ZERO = "0";
051
052    public StopWatch() throws ModelException {
053        super(StopWatch.class.getClassLoader().
054            getResource("org/apache/commons/scxml2/env/stopwatch.xml"));
055    }
056
057    // Each method below is the activity corresponding to a state in the
058    // SCXML document (see class constructor for pointer to the document).
059    public void reset() {
060        hr = min = sec = fract = dhr = dmin = dsec = dfract = 0;
061        split = false;
062    }
063
064    public void running() {
065        split = false;
066        if (timer == null) {
067            timer = new Timer(true);
068            timer.scheduleAtFixedRate(new TimerTask() {
069                @Override
070                public void run() {
071                    increment();
072                }
073            }, 100, 100);
074        }
075    }
076
077    public void paused() {
078        split = true;
079    }
080
081    public void stopped() {
082        timer.cancel();
083        timer = null;
084    }
085
086    public String getDisplay() {
087        String padhr = dhr > 9 ? EMPTY : ZERO;
088        String padmin = dmin > 9 ? EMPTY : ZERO;
089        String padsec = dsec > 9 ? EMPTY : ZERO;
090        return new StringBuffer().append(padhr).append(dhr).append(DELIM).
091            append(padmin).append(dmin).append(DELIM).append(padsec).
092            append(dsec).append(DOT).append(dfract).toString();
093    }
094
095    // used by the demonstration (see StopWatchDisplay usecase)
096    public String getCurrentState() {
097        return getEngine().getStatus().getStates().iterator().next().getId();
098    }
099
100    private void increment() {
101        if (fract < 9) {
102            fract++;
103        } else {
104            fract = 0;
105            if (sec < 59) {
106                sec++;
107            } else {
108                sec = 0;
109                if (min < 59) {
110                    min++;
111                } else {
112                    min = 0;
113                    if (hr < 99) {
114                        hr++;
115                    } else {
116                        hr = 0; //wrap
117                    }
118                }
119            }
120        }
121        if (!split) {
122            dhr = hr;
123            dmin = min;
124            dsec = sec;
125            dfract = fract;
126        }
127    }
128
129}
130