001/* A tableau for displaying Console messages.
002
003 Copyright (c) 2013-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026 */
027package ptolemy.actor.gui;
028
029import java.io.ByteArrayOutputStream;
030import java.io.FileDescriptor;
031import java.io.FileOutputStream;
032import java.io.PrintStream;
033
034import javax.swing.BoxLayout;
035import javax.swing.JPanel;
036import javax.swing.JScrollPane;
037import javax.swing.JTextArea;
038import javax.swing.text.DefaultCaret;
039
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.NamedObj;
043
044///////////////////////////////////////////////////////////////////
045//// Console
046
047/**
048 * A tableau that displays Console messages. The standard output is redirected
049 * to here.
050 *
051 * @author Liangpeng Guo
052 * @version $Id$
053 * @since Ptolemy II 10.0
054 * @Pt.ProposedRating Red (glp)
055 * @Pt.AcceptedRating Red (glp)
056 */
057public class Console extends Tableau {
058    /** Create a new control panel displaying Console
059     *  The tableau is itself an entity contained by the effigy
060     *  and having the specified name.  The frame is not made visible
061     *  automatically.  You must call show() to make it visible.
062     *  @param container The containing effigy.
063     *  @param name The name of this tableau within the specified effigy.
064     *  @exception IllegalActionException If the tableau is not acceptable
065     *   to the specified container.
066     *  @exception NameDuplicationException If the container already contains
067     *   an entity with the specified name.
068     */
069    public Console(PtolemyEffigy container, String name)
070            throws IllegalActionException, NameDuplicationException {
071        super(container, name);
072
073        ConsoleFrame frame = new ConsoleFrame(this);
074        setFrame(frame);
075    }
076
077    ///////////////////////////////////////////////////////////////////
078    ////                         inner classes                     ////
079
080    /** The frame that is created by an instance of Console.
081     */
082    @SuppressWarnings("serial")
083    public static class ConsoleFrame extends TextEditor {
084        /** Construct a frame to display Console.
085         *  After constructing this, it is necessary
086         *  to call setVisible(true) to make the frame appear.
087         *  This is typically accomplished by calling show() on
088         *  enclosing tableau.Console
089         *  @param tableau The tableau responsible for this frame.
090         *  @exception IllegalActionException If the model rejects the
091         *   configuration attribute.
092         *  @exception NameDuplicationException If a name collision occurs.
093         */
094        public ConsoleFrame(Tableau tableau)
095                throws IllegalActionException, NameDuplicationException {
096            super(tableau.getTitle());
097
098            JPanel component = new JPanel();
099            component.setLayout(new BoxLayout(component, BoxLayout.Y_AXIS));
100
101            // updateMemoryStatistics(memoryArea);
102
103            // Display total memory and free memory.
104            final JTextArea textArea = new JTextArea("", 20, 80);
105
106            ByteArrayOutputStream baos = new MyByteArrayOutputStream(textArea,
107                    tableau);
108            System.setOut(new PrintStream(baos));
109
110            textArea.setText("Standard output is redirected to here.");
111            textArea.setEditable(false);
112
113            ((DefaultCaret) textArea.getCaret())
114                    .setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
115            JScrollPane scrollPane = new JScrollPane(textArea);
116
117            add(scrollPane);
118        }
119
120        /**
121         * Redirect the standard output back when closing the frame.
122         */
123        @Override
124        protected boolean _close() {
125            System.setOut(
126                    new PrintStream(new FileOutputStream(FileDescriptor.out)));
127            System.out.println("Standard output is back.");
128            return super._close();
129        }
130
131        ///////////////////////////////////////////////////////////////////
132        ////                         inner classes                     ////
133
134        /**
135         * Customized ByteArrayOutputStream. Refresh the frame whenever new
136         * print-outs come in.
137         */
138        public static class MyByteArrayOutputStream
139                extends ByteArrayOutputStream {
140            /** Create an output stream.
141             *  @param textArea The textarea that gets updated.
142             *  @param tableau The tableau containing the text area.
143             */
144            public MyByteArrayOutputStream(JTextArea textArea,
145                    Tableau tableau) {
146                super();
147                _textArea = textArea;
148                _tableau = tableau;
149            }
150
151            @Override
152            public void write(int b) {
153                super.write(b);
154                _textArea.setText(this.toString());
155                _tableau.show();
156            }
157
158            @Override
159            public void write(byte[] b, int off, int len) {
160                super.write(b, off, len);
161                _textArea.setText(this.toString());
162                _tableau.show();
163            }
164
165            private JTextArea _textArea;
166            private Tableau _tableau;
167
168        }
169    }
170
171    /** A factory that creates a control panel to display Console.
172     */
173    public static class Factory extends TableauFactory {
174        /** Create a factory with the given name and container.
175         *  @param container The container.
176         *  @param name The name.
177         *  @exception IllegalActionException If the container is incompatible
178         *   with this attribute.
179         *  @exception NameDuplicationException If the name coincides with
180         *   an attribute already in the container.
181         */
182        public Factory(NamedObj container, String name)
183                throws IllegalActionException, NameDuplicationException {
184            super(container, name);
185        }
186
187        ///////////////////////////////////////////////////////////////////
188        ////                         public methods                    ////
189
190        /** Create a new instance of Console in the specified
191         *  effigy. If the specified effigy is not an
192         *  instance of PtolemyEffigy, then do not create a tableau
193         *  and return null. It is the responsibility of callers of
194         *  this method to check the return value and call show().
195         *
196         *  @param effigy The model effigy.
197         *  @return A new control panel tableau if the effigy is
198         *    a PtolemyEffigy, or null otherwise.
199         *  @exception Exception If the factory should be able to create a
200         *   tableau for the effigy, but something goes wrong.
201         */
202        @Override
203        public Tableau createTableau(Effigy effigy) throws Exception {
204            if (effigy instanceof PtolemyEffigy) {
205                // First see whether the effigy already contains a tableau
206                Console tableau = (Console) effigy.getEntity("ConsoleTableau");
207
208                if (tableau == null) {
209                    tableau = new Console((PtolemyEffigy) effigy,
210                            "ConsoleTableau");
211                }
212
213                return tableau;
214            } else {
215                return null;
216            }
217        }
218    }
219}