001/*
002 * Copyright (c) 1997-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2012-11-26 22:19:36 +0000 (Mon, 26 Nov 2012) $' 
007 * '$Revision: 31113 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.sdm.spa;
031
032import java.awt.Color;
033import java.awt.Container;
034import java.awt.event.ActionEvent;
035import java.awt.event.ActionListener;
036
037import javax.swing.BorderFactory;
038import javax.swing.JButton;
039import javax.swing.JFrame;
040import javax.swing.JPanel;
041import javax.swing.border.LineBorder;
042
043import ptolemy.actor.CompositeActor;
044import ptolemy.actor.Manager;
045import ptolemy.actor.lib.Transformer;
046import ptolemy.data.BooleanToken;
047import ptolemy.data.type.BaseType;
048import ptolemy.kernel.CompositeEntity;
049import ptolemy.kernel.util.IllegalActionException;
050import ptolemy.kernel.util.NameDuplicationException;
051import ptolemy.kernel.util.Nameable;
052import ptolemy.kernel.util.TransientSingletonConfigurableAttribute;
053
054//////////////////////////////////////////////////////////////////////////
055//// Pause
056/**
057 * <p>
058 * This actor is used for putting an expected pause in the workflow
059 * specification to allow for execution to pause until the outputs until that
060 * time are reviewed and the workflow is paused. This actor is mainly useful for
061 * long-running jobs.
062 * </p>
063 * 
064 * <p> Implementation of this actor is based on the
065 * ptolemy.actor.lib.Stop actor code by Edward A. Lee.
066 * </p>
067 * 
068 * @author Ilkay Altintas, Contributors: Edward A. Lee and Christopher Brooks.
069 * @version $Id: Pause.java 31113 2012-11-26 22:19:36Z crawl $
070 * @category.name flow control
071 * @category.name local
072 * @entity.description input Input content. Can be any input. They will be
073 *                     transfered to the next actor once the workflow resumes.
074 **/
075public class Pause extends Transformer {
076
077    /**
078     * Construct an actor in the specified container with the specified name.
079     * 
080     * @param container
081     *            The container.
082     * @param name
083     *            The name of this actor within the container.
084     * @exception IllegalActionException
085     *                If the actor cannot be contained by the proposed
086     *                container.
087     * @exception NameDuplicationException
088     *                If the name coincides with an actor already in the
089     *                container.
090     */
091    public Pause(CompositeEntity container, String name)
092            throws IllegalActionException, NameDuplicationException {
093        super(container, name);
094
095        input.setMultiport(true);
096        input.setTypeEquals(BaseType.BOOLEAN);
097        output.setTypeEquals(BaseType.STRING);
098        // Icon is a yield sign.
099        _attachText("_iconDescription", "<svg>\n"
100                + "<polygon points=\"0,24 24,-22 -24,-22\" "
101                + "style=\"fill:red\"/>\n" + "<text x=\"-15\" y=\"-8\""
102                + "style=\"font-size:11; fill:white; font-family:SansSerif\">"
103                + "PAUSE</text>\n" + "</svg>\n");
104        // Hide the name because the name is in the icon.
105        new TransientSingletonConfigurableAttribute(this, "_hideName");
106    }
107
108    ///////////////////////////////////////////////////////////////////
109    ////                         public methods                    ////
110
111    /** If any of the channels on the input port contain a true token,
112     *  the cause postfire() to pause the Manager, display a dialog
113     *  with a button to resume execution and send a true token.
114     *  @exception IllegalActionException If there is a problem
115     *  reading the input channels or sending the token to the output.
116     */
117    public void fire() throws IllegalActionException {
118        super.fire();
119        _sawTrueInput = false;
120        if (!input.isOutsideConnected()) {
121            _sawTrueInput = true;
122        }
123        // NOTE: We need to consume data on all channels that have data.
124        // If we don't then DE will go into an infinite loop.
125        for (int i = 0; i < input.getWidth(); i++) {
126            if (input.hasToken(i)) {
127                if (((BooleanToken) input.get(i)).booleanValue()) {
128                    _sawTrueInput = true;
129                }
130            }
131        }
132    }
133    
134
135    /** If we read a true token, then pause the Manager until the
136     *  user presses a button.  If nothing at all is connected to the
137     *  input port, then pause unconditionaly.
138     *  @return the value returned by the postfire() method in the
139     *  superclass.  Typically, true is returned if the iteration
140     *  may continue.  If stop was requested, then the superclass
141     *  usually returns false.
142     *  @exception IllegalActionException If there is no Manager,
143     *  if the container is not a CompositeActor or if the
144     *  there is some other problem.
145     */
146    public boolean postfire() throws IllegalActionException {
147        if (_debugging) {
148            _debug("Pause.postfire(): _sawTrueInput: " + _sawTrueInput);
149        }
150        if (_sawTrueInput) {
151            // Create the UI each time we get a true on an input channel.
152            _resumeButton = new JButton("Resume model execution.");
153            _resumeButton.addActionListener(new ActionListener() {
154                    public void actionPerformed(ActionEvent e) {
155                        if (_debugging) {
156                            _debug("Pause.actionPerformed()");
157                        }
158                        if (_manager != null) {
159                            _debug("Pause.actionPerformed(): resuming manager");
160                            _manager.resume();
161                        } 
162                        _frame.dispose();
163                    }
164                });
165
166            _panel = new JPanel();
167            _panel.add(_resumeButton);
168
169            if (_container == null) {
170                _frame = new JFrame(getFullName());
171                _frame.getContentPane().add(_panel);
172                _frame.setSize(200, 250);
173            } else {
174                _container.add(_panel);
175                _panel.setBackground(null);
176                _panel.setBorder(new LineBorder(Color.black));
177            }
178            _panel.setBorder(BorderFactory
179                    .createTitledBorder("The border title goes here..."));
180
181            if (_frame != null) {
182                _frame.setVisible(true);
183            }
184
185            Nameable container = getContainer();
186            if (container instanceof CompositeActor) {
187                _manager = ((CompositeActor) container).getManager();
188                if (_manager != null) {
189                    if (_debugging) {
190                        _debug("Pause.postfire(): pausing Manager()");
191                    }
192                    _manager.pause();
193                } else {
194                    throw new IllegalActionException(this,
195                            "Cannot pause without a Manager.");
196                }
197            } else {
198                throw new IllegalActionException(this,
199                        "Cannot pause without a container that is a CompositeActor.");
200            }
201        }
202        if (_debugging) {
203            _debug("Pause.postfire(): _sawTrueInput: " + _sawTrueInput);
204        }
205
206        // It is safer to do output in postfire() in case this method
207        // is inside a Continuous director.
208        output.send(0, new BooleanToken(_sawTrueInput));
209        return super.postfire();
210    }
211
212    /** Dispose the frame of the button dialog.
213     *  @exception IllegalActionException Not thrown in this base class.
214     */   
215    public void wrapup() throws IllegalActionException {
216        if (_frame != null) {
217            _frame.dispose();
218        }
219    }
220
221    ///////////////////////////////////////////////////////////////////
222    ////                         protected variables               ////
223
224    protected JPanel _panel;
225
226    ///////////////////////////////////////////////////////////////////
227    ////                         private variables                 ////
228
229    private Container _container;
230
231    /** The frame into which to put the button, if any. */
232    private JFrame _frame;
233
234    private Manager _manager;
235
236    private JButton _resumeButton;
237
238    private boolean _sawTrueInput;
239}