001/* A top-level dialog window for editing Unit constraints.
002
003 Copyright (c) 2003-2016 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.vergil.unit;
028
029import java.awt.BorderLayout;
030import java.awt.Color;
031import java.awt.Frame;
032import java.awt.event.ActionEvent;
033import java.net.URL;
034import java.util.HashSet;
035import java.util.Iterator;
036import java.util.Set;
037import java.util.Vector;
038
039import javax.swing.AbstractListModel;
040import javax.swing.BorderFactory;
041import javax.swing.BoxLayout;
042import javax.swing.JButton;
043import javax.swing.JLabel;
044import javax.swing.JList;
045import javax.swing.JPanel;
046import javax.swing.JScrollPane;
047import javax.swing.ListSelectionModel;
048import javax.swing.event.ListSelectionEvent;
049import javax.swing.event.ListSelectionListener;
050
051import diva.canvas.Figure;
052import diva.canvas.interactor.BasicSelectionRenderer;
053import diva.canvas.interactor.Interactor;
054import diva.canvas.interactor.SelectionEvent;
055import diva.canvas.interactor.SelectionInteractor;
056import diva.canvas.interactor.SelectionListener;
057import diva.canvas.interactor.SelectionModel;
058import diva.canvas.interactor.SelectionRenderer;
059import diva.graph.GraphController;
060import diva.graph.GraphPane;
061import diva.graph.GraphUtilities;
062import diva.graph.JGraph;
063import ptolemy.actor.TypedCompositeActor;
064import ptolemy.actor.gui.Configuration;
065import ptolemy.actor.gui.DialogTableau;
066import ptolemy.actor.gui.PtolemyDialog;
067import ptolemy.actor.gui.Tableau;
068import ptolemy.actor.gui.TableauFrame;
069import ptolemy.kernel.ComponentEntity;
070import ptolemy.kernel.Entity;
071import ptolemy.kernel.Port;
072import ptolemy.kernel.Relation;
073import ptolemy.kernel.util.Attribute;
074import ptolemy.kernel.util.IllegalActionException;
075import ptolemy.kernel.util.Location;
076import ptolemy.kernel.util.NamedObj;
077import ptolemy.moml.MoMLChangeRequest;
078import ptolemy.moml.unit.Solution;
079import ptolemy.moml.unit.UnitConstraints;
080import ptolemy.util.MessageHandler;
081import ptolemy.vergil.basic.AbstractBasicGraphModel;
082import ptolemy.vergil.basic.BasicGraphFrame;
083
084///////////////////////////////////////////////////////////////////
085//// UnitSolverDialog
086
087/**
088 Dialog for the Unit Solver.
089
090 @author Rowland R Johnson
091 @version $Id$
092 @since Ptolemy II 8.0
093 @Pt.ProposedRating Red (rowland)
094 @Pt.AcceptedRating Red (rowland)
095 */
096@SuppressWarnings("serial")
097public class UnitSolverDialog extends PtolemyDialog
098        implements ListSelectionListener, SelectionListener {
099    /**
100     * Construct a Unit Solver Dialog.
101     * @param dialogTableau The DialogTableau.
102     * @param owner The object that, per the user, appears to be generating the
103     * dialog.
104     * @param target The object whose units are being solved.
105     * @param configuration The configuration to use to open the help screen.
106     */
107    public UnitSolverDialog(DialogTableau dialogTableau, Frame owner,
108            Entity target, Configuration configuration) {
109        super("Solve units for " + target.getName(), dialogTableau, owner,
110                target, configuration);
111
112        SelectionRenderer tempSelectionRenderer = null;
113        _tableau = ((TableauFrame) owner).getTableau();
114
115        _model = (TypedCompositeActor) target;
116
117        // ((TypedCompositeActor) (((PtolemyEffigy) (_tableau.getContainer()))
118        //      .getModel()));
119        BasicGraphFrame parent = (BasicGraphFrame) _tableau.getFrame();
120        JGraph jGraph = parent.getJGraph();
121        GraphPane graphPane = jGraph.getGraphPane();
122        _controller = graphPane.getGraphController();
123        _selectionModel = _controller.getSelectionModel();
124
125        Interactor interactor = _controller.getEdgeController(new Object())
126                .getEdgeInteractor();
127        _graphModel = (AbstractBasicGraphModel) _controller.getGraphModel();
128        _selectionInteractor = (SelectionInteractor) interactor;
129        _defaultSelectionRenderer = _selectionInteractor.getSelectionRenderer();
130        tempSelectionRenderer = new BasicSelectionRenderer(
131                new BasicEdgeHighlighter());
132
133        if (_model == getTarget()) {
134            _entities = _getSelectedNodes();
135            _relations = _getSelectedRelations();
136
137            if (_entities.isEmpty() && _relations.isEmpty()) {
138                _entities = new HashSet<ComponentEntity>(
139                        _model.entityList(ComponentEntity.class));
140                _relations = new HashSet<Relation>(_model.relationList());
141            }
142        } else {
143            _entities = new HashSet<ComponentEntity>();
144            Entity targetEntity = getTarget();
145            if (targetEntity instanceof ComponentEntity) {
146                _entities.add((ComponentEntity) targetEntity);
147            }
148            _relations = new HashSet<Relation>();
149        }
150
151        _selectionModel.clearSelection();
152        _selectionInteractor.setSelectionRenderer(tempSelectionRenderer);
153        _showComponents();
154        _selectionModel.addSelectionListener(this);
155
156        JPanel fullSolverPanel = new JPanel();
157        fullSolverPanel
158                .setLayout(new BoxLayout(fullSolverPanel, BoxLayout.Y_AXIS));
159        fullSolverPanel.setBorder(BorderFactory.createCompoundBorder(
160                BorderFactory.createTitledBorder("Full Solution"),
161                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
162        _runFullSolverButton.addActionListener(this);
163        fullSolverPanel.add(_runFullSolverButton);
164        _fullSolutionResult.setOpaque(true);
165        _fullSolutionResult.setBackground(Color.white);
166        fullSolverPanel.add(_fullSolutionResult);
167
168        JPanel componentsPanel = new JPanel();
169        componentsPanel
170                .setLayout(new BoxLayout(componentsPanel, BoxLayout.Y_AXIS));
171        componentsPanel.setBorder(BorderFactory.createCompoundBorder(
172                BorderFactory.createTitledBorder("Components"),
173                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
174        _setToSelectedButton.setEnabled(false);
175        componentsPanel.add(_setToSelectedButton);
176        _setToSelectedButton.addActionListener(this);
177        componentsPanel.add(_showComponentsButton);
178        _showComponentsButton.addActionListener(this);
179
180        JPanel topPanel = new JPanel();
181        topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));
182        topPanel.add(fullSolverPanel);
183        topPanel.add(componentsPanel);
184
185        JPanel minimalSpanPanel = new JPanel(new BorderLayout());
186        minimalSpanPanel.setBorder(BorderFactory.createCompoundBorder(
187                BorderFactory.createTitledBorder("Minimal Spanning Solutions"),
188                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
189        minimalSpanPanel.add(_runMinimalSpanSolverButton, BorderLayout.NORTH);
190        _runMinimalSpanSolverButton.addActionListener(this);
191        _solutionsListModel = new SolutionListModel();
192        _solutionsList = new JList(_solutionsListModel);
193        _solutionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
194        _solutionsList.addListSelectionListener(this);
195
196        JScrollPane scrollPane = new JScrollPane(_solutionsList);
197        minimalSpanPanel.add(scrollPane, BorderLayout.CENTER);
198
199        JPanel mainPane = new JPanel();
200        mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS));
201        mainPane.add(topPanel);
202        mainPane.add(minimalSpanPanel);
203        setContents(mainPane);
204    }
205
206    ///////////////////////////////////////////////////////////////////
207    ////                         public methods                    ////
208
209    /* (non-Javadoc)
210     * @see java.awt.event.ActionListener#actionPerformed(ActionEvent)
211     */
212    @Override
213    public void actionPerformed(ActionEvent aEvent) {
214        //String command = aEvent.getActionCommand();
215        if (aEvent.getSource() == _runMinimalSpanSolverButton) {
216            try {
217                // FIXME: UnitContstraint ctor should take args other than Vectors.
218                _uConstraints = new UnitConstraints(_model,
219                        new Vector(_entities), new Vector(_relations));
220                _solutions = _uConstraints.minimalSpanSolutions();
221            } catch (IllegalActionException e) {
222                MessageHandler.error("Minimal Span Solver failed: ", e);
223                return;
224            }
225
226            _solutionsListModel.setSolutions(_solutions);
227            _solutionsList.setModel(_solutionsListModel);
228        } else if (aEvent.getSource() == _runFullSolverButton) {
229            _solutionsList.clearSelection();
230
231            try {
232                _uConstraints = new UnitConstraints(_model,
233                        new Vector(_entities), new Vector(_relations));
234
235                Solution solution = _uConstraints.completeSolution();
236                _fullSolutionResult.setText(solution.getShortStateDesc());
237
238                //solution.trace();
239            } catch (IllegalActionException e) {
240                MessageHandler.error("Full Solver failed: ", e);
241                return;
242            }
243        } else if (aEvent.getSource() == _setToSelectedButton) {
244            _setSelectedComponents();
245        } else if (aEvent.getSource() == _showComponentsButton) {
246            _showComponents();
247        } else {
248            super.actionPerformed(aEvent);
249        }
250    }
251
252    /** Remove all the annotations from the graph.
253     * Actors, their ports, and relations are inspected to see if they either a
254     * _color and/or an _explanation attribute. If so, then the attribute is
255     * removed via a MoMl changeRequest.
256     */
257    public void deAnnotateGraph() {
258        StringBuffer moml = new StringBuffer();
259        Iterator entities = _model.entityList(ComponentEntity.class).iterator();
260
261        while (entities.hasNext()) {
262            ComponentEntity entity = (ComponentEntity) entities.next();
263            String entityDeletes = _deletesIfNecessary(entity);
264            moml.append("<entity name=\"" + entity.getName() + "\">");
265
266            if (entityDeletes != null) {
267                moml.append(entityDeletes);
268            }
269
270            Iterator ports = entity.portList().iterator();
271
272            while (ports.hasNext()) {
273                Port port = (Port) ports.next();
274                String portDeletes = _deletesIfNecessary(port);
275
276                if (portDeletes != null) {
277                    moml.append("<port name=\"" + port.getName() + "\">"
278                            + portDeletes + "</port>");
279                }
280            }
281
282            moml.append("</entity>");
283        }
284
285        Iterator relations = _model.relationList().iterator();
286
287        while (relations.hasNext()) {
288            Relation relation = (Relation) relations.next();
289            String relationDeletes = _deletesIfNecessary(relation);
290
291            if (relationDeletes != null) {
292                moml.append("<relation name=\"" + relation.getName() + "\">"
293                        + relationDeletes + "\"/></relation>");
294            }
295        }
296
297        if (moml.length() > 0) {
298            String momlUpdate = "<group>" + moml.toString() + "</group>";
299            MoMLChangeRequest request = new MoMLChangeRequest(this, _model,
300                    momlUpdate);
301            request.setUndoable(true);
302            request.setPersistent(false);
303
304            _model.requestChange(request);
305        }
306    }
307
308    /* (non-Javadoc)
309     * @see diva.canvas.interactor
310     *                 .SelectionListener#selectionChanged(SelectionEvent)
311     */
312    @Override
313    public void selectionChanged(SelectionEvent e) {
314        _setToSelectedButton.setEnabled(true);
315    }
316
317    /* (non-Javadoc)
318     * @see javax.swing.event
319     *               .ListSelectionListener#valueChanged(ListSelectionEvent)
320     */
321    @Override
322    public void valueChanged(ListSelectionEvent e) {
323        if (e.getValueIsAdjusting()) {
324            return;
325        }
326
327        int index = _solutionsList.getSelectedIndex();
328
329        if (index >= 0) {
330            _showComponents();
331
332            Solution solution = (Solution) _solutions.elementAt(index);
333            solution.annotateGraph();
334
335            //solution.trace();
336        }
337    }
338
339    /** List of solutions.
340     */
341    public static class SolutionListModel extends AbstractListModel {
342        Vector _solutions = new Vector();
343
344        /** Return an element.
345         *  @param index The index of the element to be returned.
346         *  @return The element at the specified index.
347         */
348        @Override
349        public Object getElementAt(int index) {
350            return ((Solution) _solutions.elementAt(index)).getStateDesc();
351        }
352
353        /** Return the number of solutions.
354         *  @return The number of solutions.
355         */
356        @Override
357        public int getSize() {
358            return _solutions.size();
359        }
360
361        /** Set the solutions to the specified argument.
362         *  @param solutions A vector of solutions.
363         */
364        public void setSolutions(Vector solutions) {
365            _solutions = solutions;
366            fireContentsChanged(this, 0, _solutions.size());
367        }
368
369        /** Clear the current set of solutions.
370         */
371        public void clear() {
372            _solutions = new Vector();
373        }
374    }
375
376    ///////////////////////////////////////////////////////////////////
377    ////                         protected methods                 ////
378    @Override
379    protected void _cancel() {
380        _selectionModel.removeSelectionListener(this);
381        _selectionModel.clearSelection();
382        _selectionInteractor.setSelectionRenderer(_defaultSelectionRenderer);
383        _showComponents();
384        super._cancel();
385    }
386
387    /* (non-Javadoc)
388     * @see ptolemy.actor.gui.PtolemyDialog#_createExtendedButtons(JPanel)
389     */
390    @Override
391    protected void _createExtendedButtons(JPanel _buttons) {
392    }
393
394    @Override
395    protected URL _getHelpURL() {
396        URL helpURL = getClass().getClassLoader()
397                .getResource("ptolemy/actor/gui/doc/unitConstraintsSolver.htm");
398        return helpURL;
399    }
400
401    ///////////////////////////////////////////////////////////////////
402    ////                         private methods                   ////
403    private String _deletesIfNecessary(NamedObj obj) {
404        String retv = null;
405        Attribute color = obj.getAttribute("_color");
406        Attribute explanation = obj.getAttribute("_explanation");
407
408        if (color != null && explanation != null) {
409            retv = "<deleteProperty name=\"_color\"/>"
410                    + "<deleteProperty name=\"_explanation\"/>";
411        }
412
413        return retv;
414    }
415
416    /** Create a Vector of selected nodes in a Tableau. This method really
417     *  belongs elsewhere and will be moved there at some point.
418     *  @return Vector of selected Nodes.
419     */
420    private Set<ComponentEntity> _getSelectedNodes() {
421        Set<ComponentEntity> nodes = new HashSet<ComponentEntity>();
422
423        if (_tableau.getFrame() instanceof BasicGraphFrame) {
424            Object[] selection = _selectionModel.getSelectionAsArray();
425
426            for (Object element : selection) {
427                if (element instanceof Figure) {
428                    Object userObject = ((Figure) element).getUserObject();
429                    NamedObj actual = (NamedObj) _graphModel
430                            .getSemanticObject(userObject);
431
432                    if (actual instanceof ComponentEntity) {
433                        nodes.add((ComponentEntity) actual);
434                    }
435                }
436            }
437        }
438
439        return nodes;
440    }
441
442    /** Create a Vector of selected Relations in a Tableau. This method really
443     *  belongs elsewhere and will be moved there at some point.
444     *  @return Vector of selected Relations.
445     */
446    private Set<Relation> _getSelectedRelations() {
447        Set<Relation> relations = new HashSet<Relation>();
448
449        if (_tableau.getFrame() instanceof BasicGraphFrame) {
450            Object[] selection = _selectionModel.getSelectionAsArray();
451
452            for (Object element : selection) {
453                if (element instanceof Figure) {
454                    Object userObject = ((Figure) element).getUserObject();
455                    NamedObj actual = (NamedObj) _graphModel
456                            .getSemanticObject(userObject);
457
458                    if (actual instanceof Relation
459                            && !relations.contains(actual)) {
460                        relations.add((Relation) actual);
461                    }
462                }
463            }
464        }
465
466        return relations;
467    }
468
469    /**
470     *
471     */
472    private void _setSelectedComponents() {
473        // FIXME: Why don't we just set _entities and _relations here?
474        Set<ComponentEntity> entities = _getSelectedNodes();
475        Set<Relation> relations = _getSelectedRelations();
476        _entities = new HashSet<ComponentEntity>(entities);
477        _relations = new HashSet<Relation>(relations);
478
479        //         for (int i = 0; i < entities.size(); i++) {
480        //             _entities.add(entities.elementAt(i));
481        //         }
482
483        //         for (int i = 0; i < relations.size(); i++) {
484        //             _relations.add(relations.elementAt(i));
485        //         }
486
487        _setToSelectedButton.setEnabled(false);
488    }
489
490    /**
491     *
492     */
493    private void _showComponents() {
494        _selectionModel.clearSelection();
495        deAnnotateGraph();
496
497        Iterator nodes = _graphModel.nodes(_model);
498
499        while (nodes.hasNext()) {
500            Location node = (Location) nodes.next();
501            NamedObj entity = (NamedObj) _graphModel.getSemanticObject(node);
502
503            if (_entities.contains(entity)) {
504                Figure figure = _controller.getFigure(node);
505                _selectionModel.addSelection(figure);
506
507                Iterator edges = GraphUtilities.partiallyContainedEdges(node,
508                        _graphModel);
509
510                while (edges.hasNext()) {
511                    Object edge = edges.next();
512                    Object relation = _graphModel.getSemanticObject(edge);
513
514                    if (relation instanceof Relation
515                            && _relations.contains(relation)) {
516                        Figure relationFigure = _controller.getFigure(edge);
517                        _selectionModel.addSelection(relationFigure);
518                    }
519                }
520            }
521        }
522    }
523
524    ///////////////////////////////////////////////////////////////////
525    ////                         private variables                 ////
526    GraphController _controller = null;
527
528    SelectionRenderer _defaultSelectionRenderer = null;
529
530    Set<ComponentEntity> _entities = null;
531
532    JLabel _fullSolutionResult = new JLabel("Not Run");
533
534    JButton _setToSelectedButton = new JButton("Set To Selected");
535
536    JButton _showComponentsButton = new JButton("Show Components");
537
538    TypedCompositeActor _model = null;
539
540    SelectionModel _selectionModel = null;
541
542    AbstractBasicGraphModel _graphModel = null;
543
544    Set<Relation> _relations = null;
545
546    SelectionInteractor _selectionInteractor = null;
547
548    Vector _solutions = new Vector();
549
550    JList _solutionsList = null;
551
552    SolutionListModel _solutionsListModel = null;
553
554    JButton _runMinimalSpanSolverButton = new JButton("Run");
555
556    JButton _runFullSolverButton = new JButton("Run");
557
558    Tableau _tableau = null;
559
560    UnitConstraints _uConstraints = null;
561}