001/* Static methods for manipulating Ptolemy models. 002@Copyright (c) 2009-2016 The Regents of the University of California. 003All rights reserved. 004 005Permission is hereby granted, without written agreement and without 006license or royalty fees, to use, copy, modify, and distribute this 007software and its documentation for any purpose, provided that the 008above copyright notice and the following two paragraphs appear in all 009copies of this software. 010 011IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015SUCH DAMAGE. 016 017THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 028 */ 029 030package ptolemy.vergil.basic.layout.kieler; 031 032import java.util.HashSet; 033import java.util.List; 034import java.util.Set; 035 036import javax.swing.SwingConstants; 037 038import ptolemy.actor.Actor; 039import ptolemy.actor.IOPort; 040import ptolemy.domains.modal.kernel.State; 041import ptolemy.kernel.ComponentPort; 042import ptolemy.kernel.CompositeEntity; 043import ptolemy.kernel.Port; 044import ptolemy.kernel.Relation; 045import ptolemy.kernel.util.Attribute; 046import ptolemy.kernel.util.Locatable; 047import ptolemy.kernel.util.NamedObj; 048import ptolemy.vergil.actor.KielerLayoutUtil; 049import ptolemy.vergil.basic.RelativeLocatable; 050import ptolemy.vergil.basic.RelativeLocation; 051 052/////////////////////////////////////////////////////////////////// 053//// PtolemyModelUtil 054/** 055 * Utility class for accessing properties of a Ptolemy model in the context 056 * of automatic layout. The methods are used by the KIELER layout bridge 057 * that integrates KIELER layout algorithms into Ptolemy. 058 * 059 * @author Hauke Fuhrmann (<a href="mailto:haf@informatik.uni-kiel.de">haf</a>), 060 * Christian Motika (<a href="mailto:cmot@informatik.uni-kiel.de">cmot</a>), 061 * Miro Spoenemann (<a href="mailto:msp@informatik.uni-kiel.de">msp</a>), 062 * Ulf Rueegg 063 * @version $Id$ 064 * @since Ptolemy II 8.0 065 * @Pt.ProposedRating Red (cxh) 066 * @Pt.AcceptedRating Red (cxh) 067 */ 068public final class PtolemyModelUtil { 069 070 /////////////////////////////////////////////////////////////////// 071 //// protected methods //// 072 073 /** 074 * For a set of relations get a set of relation groups, i.e. for each 075 * relation construct a list of relations that are all interconnected, 076 * either directly or indirectly. 077 * 078 * @param relations Set of relations 079 * @return a Set of relation groups as given by List <Relation> objects by Ptolemy 080 */ 081 protected static Set<List<Relation>> _getRelationGroups( 082 Set<Relation> relations) { 083 Set<List<Relation>> relationGroups = new HashSet<List<Relation>>(); 084 for (Relation relation : relations) { 085 List<Relation> relationGroup = relation.relationGroupList(); 086 // check if we already have this relation group 087 // TODO: verify whether relation groups are unique. Then you could 088 // perform this check much more efficiently 089 boolean found = false; 090 for (List<Relation> listedRelationGroup : relationGroups) { 091 if (listedRelationGroup.containsAll(relationGroup)) { 092 found = true; 093 break; 094 } 095 } 096 if (!found) { 097 relationGroups.add(relationGroup); 098 } 099 } 100 return relationGroups; 101 } 102 103 /** 104 * Check whether the given Ptolemy model object has any connections, i.e. is 105 * connected to any other components via some link. 106 * 107 * @param namedObj The Ptolemy model object which is to be analyzed 108 * @return True if the object is an Actor and any port has any relations or 109 * is connected to any other port; true if the object is a Relation; 110 * false if the object is an Attribute. Defaults to false. 111 */ 112 protected static boolean _isConnected(NamedObj namedObj) { 113 if (namedObj instanceof RelativeLocatable) { 114 // Relative locatables may be connected to a reference object. 115 Locatable locatable = KielerLayoutUtil.getLocation(namedObj); 116 if (locatable instanceof RelativeLocation) { 117 NamedObj referenceObj = _getReferencedObj( 118 (RelativeLocation) locatable); 119 return referenceObj != null && _isConnected(referenceObj); 120 } 121 } 122 if (namedObj instanceof Attribute) { 123 return false; 124 } 125 if (namedObj instanceof Actor) { 126 Actor actor = (Actor) namedObj; 127 // If any port of an actor is connected to any other 128 // assume that there is also no visible connection. 129 for (Object o : actor.inputPortList()) { 130 Port port = (Port) o; 131 if (!port.connectedPortList().isEmpty() 132 || !port.linkedRelationList().isEmpty()) { 133 return true; 134 } 135 } 136 for (Object o : actor.outputPortList()) { 137 Port port = (Port) o; 138 if (!port.connectedPortList().isEmpty() 139 || !port.linkedRelationList().isEmpty()) { 140 return true; 141 } 142 } 143 return false; 144 } 145 if (namedObj instanceof Relation) { 146 return true; 147 } 148 if (namedObj instanceof ComponentPort) { 149 ComponentPort port = (ComponentPort) namedObj; 150 return !port.insidePortList().isEmpty() 151 || !port.insideRelationList().isEmpty(); 152 } 153 if (namedObj instanceof State) { 154 State state = (State) namedObj; 155 return !state.getIncomingPort().connectedPortList().isEmpty() 156 || !state.getOutgoingPort().connectedPortList().isEmpty(); 157 } 158 // default to false 159 return false; 160 } 161 162 /** 163 * Find the reference object for the given relative location. 164 * 165 * @param location A relative location 166 * @return The corresponding reference object, or null if there is none 167 */ 168 protected static NamedObj _getReferencedObj(RelativeLocation location) { 169 NamedObj container = location.getContainer(); 170 if (container != null) { 171 NamedObj containersContainer = container.getContainer(); 172 if (containersContainer instanceof CompositeEntity) { 173 CompositeEntity composite = (CompositeEntity) containersContainer; 174 String relativeToName = location.relativeTo.getExpression(); 175 String elementName = location.relativeToElementName 176 .getExpression(); 177 // The relativeTo object is not necessarily an Entity. 178 NamedObj relativeToNamedObj; 179 if (elementName.equals("property")) { 180 relativeToNamedObj = composite.getAttribute(relativeToName); 181 } else if (elementName.equals("port")) { 182 relativeToNamedObj = composite.getPort(relativeToName); 183 } else if (elementName.equals("relation")) { 184 relativeToNamedObj = composite.getRelation(relativeToName); 185 } else { 186 relativeToNamedObj = composite.getEntity(relativeToName); 187 } 188 return relativeToNamedObj; 189 } 190 } 191 return null; 192 } 193 194 /** 195 * Determine whether a given Port is an input port. 196 * 197 * @param port The port to be analyzed 198 * @return True if the port is an input port 199 */ 200 protected static boolean _isInput(Port port) { 201 if (port instanceof IOPort) { 202 return ((IOPort) port).isInput(); 203 } 204 NamedObj obj = port.getContainer(); 205 if (obj instanceof Actor) { 206 Actor actor = (Actor) obj; 207 if (actor.inputPortList().contains(port)) { 208 return true; 209 } 210 } 211 return false; 212 } 213 214 /** 215 * Get the direction of the edge anchor point of an external port 216 * inside a composite actor. It is given as a {@link SwingConstants}, like 217 * {@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}, 218 * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}. 219 * @param port the external port 220 * @return a SwingConstant about the direction 221 */ 222 protected static int _getExternalPortDirection(Port port) { 223 if (port instanceof IOPort) { 224 boolean isInput = ((IOPort) port).isInput(); 225 boolean isOutput = ((IOPort) port).isOutput(); 226 if (isInput && !isOutput) { 227 return SwingConstants.EAST; 228 } 229 if (!isInput && isOutput) { 230 return SwingConstants.WEST; 231 } 232 if (isInput && isOutput) { 233 return SwingConstants.NORTH; 234 } 235 } 236 return SwingConstants.SOUTH; 237 } 238 239 /** 240 * Return true if the state is an initial state. 241 * @param state a {@link State} to test 242 * @return whether the passed state has the isInitialState parameter set to true. 243 */ 244 protected static boolean _isInitialState(State state) { 245 return state.isInitialState.getValueAsString() 246 .equals(Boolean.TRUE.toString()); 247 } 248 249 /** 250 * Return true if the state is a final state. 251 * @param state a {@link State} to test 252 * @return whether the passed state has the isFinalState parameter set to true. 253 */ 254 protected static boolean _isFinalState(State state) { 255 return state.isFinalState.getValueAsString() 256 .equals(Boolean.TRUE.toString()); 257 } 258}