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 &lt;Relation&gt; 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}