001/* The default KIELER layout with place and route. 002 003 Copyright (c) 2010-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 2 027 */ 028package ptolemy.vergil.basic.layout.kieler; 029 030import java.util.Iterator; 031import java.util.List; 032 033import javax.swing.JFrame; 034 035import diva.graph.GraphController; 036import diva.graph.GraphModel; 037import diva.graph.basic.BasicLayoutTarget; 038import diva.util.Filter; 039import ptolemy.actor.CompositeActor; 040import ptolemy.actor.gui.Configuration; 041import ptolemy.actor.gui.Effigy; 042import ptolemy.actor.gui.Tableau; 043import ptolemy.data.BooleanToken; 044import ptolemy.domains.modal.kernel.FSMActor; 045import ptolemy.kernel.CompositeEntity; 046import ptolemy.kernel.util.IllegalActionException; 047import ptolemy.kernel.util.InternalErrorException; 048import ptolemy.kernel.util.NamedObj; 049import ptolemy.util.MessageHandler; 050import ptolemy.vergil.actor.ActorGraphFrame; 051import ptolemy.vergil.basic.BasicGraphFrame; 052import ptolemy.vergil.basic.IGuiAction; 053import ptolemy.vergil.basic.PtolemyLayoutAction; 054import ptolemy.vergil.basic.layout.AbstractLayoutConfiguration; 055import ptolemy.vergil.modal.FSMGraphFrame; 056 057/////////////////////////////////////////////////////////////////// 058//// KielerLayoutAction 059 060/** 061 * Trigger the KIELER place and route automatic dataflow layout algorithm 062 * from within the Vergil GUI. Operate on the current model, hence the 063 * model needs to be an input in the doAction() method. 064 * <p> 065 * This action implements the {@link Filter} interface to check whether 066 * a given model is supported. 067 * </p> 068 * 069 * @author Christian Motika 070 * @version $Id$ 071 * @since Ptolemy II 10.0 072 * @Pt.ProposedRating Red (cmot) 073 * @Pt.AcceptedRating Red (cmot) 074 */ 075public class KielerLayoutAction extends Object implements IGuiAction, Filter { 076 077 /////////////////////////////////////////////////////////////////// 078 //// public methods //// 079 080 /** 081 * Layout the graph if the model is a CompositeActor. Otherwise throw an 082 * exception. The frame type must be ActorGraphFrame. The KIELER layouter 083 * is called with placing and routing. The routing uses bend point 084 * annotations. 085 * 086 * @param model the model 087 */ 088 @Override 089 public void doAction(NamedObj model) { 090 try { 091 if (!accept(model)) { 092 throw new InternalErrorException( 093 "For now only actor models and modal models are supported by KIELER layout. " 094 + "The model \"" + model.getFullName() 095 + "\" is a " + model.getClass().getName() 096 + " which is not supported yet."); 097 } 098 JFrame frame = null; 099 int tableauxCount = 0; 100 Effigy effigy = Configuration.findEffigy(model); 101 if (effigy == null) { 102 effigy = Configuration.findEffigy(model.getContainer()); 103 } 104 if (effigy != null) { 105 Iterator tableaux = effigy.entityList(Tableau.class).iterator(); 106 while (tableaux.hasNext()) { 107 Tableau tableau = (Tableau) tableaux.next(); 108 tableauxCount++; 109 if (tableau.getFrame() instanceof ActorGraphFrame 110 || tableau.getFrame() instanceof FSMGraphFrame) { 111 frame = tableau.getFrame(); 112 break; 113 } 114 } 115 } 116 // Check for supported type of editor. 117 if (frame == null) { 118 String message = ""; 119 if (tableauxCount == 0) { 120 message = "findEffigy() found no Tableaux."; 121 } else if (effigy != null) { 122 JFrame firstFrame = effigy.entityList(Tableau.class).get(0) 123 .getFrame(); 124 message = "The first frame of " + tableauxCount 125 + " found by findEffigy() is " 126 + (firstFrame == null ? "null" 127 : "a \"" + firstFrame.getClass().getName() 128 + "\"") 129 + ", which is not an ActorGraphFrame or FSMGraphFrame."; 130 } 131 throw new InternalErrorException(model, null, 132 "For now only actor models and modal models are supported by KIELER layout. " 133 + message); 134 } else { 135 BasicGraphFrame graphFrame = (BasicGraphFrame) frame; 136 137 // Check if the old layout algorithm should be used 138 if (_useOldAlgorithm(model)) { 139 new PtolemyLayoutAction().doAction(model); 140 } else { 141 // Fetch everything needed to build the LayoutTarget. 142 GraphController graphController = graphFrame.getJGraph() 143 .getGraphPane().getGraphController(); 144 GraphModel graphModel = graphFrame.getJGraph() 145 .getGraphPane().getGraphController() 146 .getGraphModel(); 147 BasicLayoutTarget layoutTarget = new BasicLayoutTarget( 148 graphController); 149 150 // Create KIELER layouter for this layout target. 151 KielerLayout layout = new KielerLayout(layoutTarget); 152 // the cast is save because of the #accept() method 153 layout.setModel((CompositeEntity) model); 154 layout.setTop(graphFrame); 155 156 layout.layout(graphModel.getRoot()); 157 } 158 } 159 } catch (Throwable throwable) { 160 // Catch a throwable in case the guava classes are not found. 161 162 // If we do not catch throwables here, then they 163 // disappear to stdout, which is bad if we launched 164 // where there is no stdout visible. 165 MessageHandler.error("Failed to layout \"" 166 + (model == null ? "name not found" : model.getFullName()) 167 + "\"", throwable); 168 } 169 } 170 171 /** 172 * Check whether the given model is supported by this layout action. 173 * 174 * @param o The object to be be checked. 175 * @return true if the model can be laid out with this action. 176 */ 177 @Override 178 public boolean accept(Object o) { 179 return o instanceof CompositeActor || o instanceof FSMActor; 180 } 181 182 /////////////////////////////////////////////////////////////////// 183 //// private methods //// 184 185 /** 186 * Checks if the given model is configured to use Ptolemy's old layout algorithm. 187 * 188 * @param model the model to check. 189 * @return {@code true} if the model has a layout configuration that explicitly 190 * instructs us to use the old layout algorithm. 191 */ 192 private boolean _useOldAlgorithm(NamedObj model) { 193 try { 194 // Find the model's LayoutConfiguration element 195 List<AbstractLayoutConfiguration> configAttributes = model 196 .attributeList(AbstractLayoutConfiguration.class); 197 198 // If there is such an element, check if the old algorithm is to be used 199 if (!configAttributes.isEmpty()) { 200 AbstractLayoutConfiguration configuration = configAttributes 201 .get(0); 202 203 BooleanToken useOldAlgorithm = BooleanToken 204 .convert(configuration.useOldAlgorithm.getToken()); 205 if (useOldAlgorithm.booleanValue()) { 206 return true; 207 } 208 } 209 } catch (IllegalActionException e) { 210 // Ignore exception -- we'll return false 211 } 212 213 // Default to false 214 return false; 215 } 216 217}