001/* Static helper class to work with KIELER graph datastructures. */
002/*
003@Copyright (c) 2009-2014 The Regents of the University of California.
004All rights reserved.
005
006Permission is hereby granted, without written agreement and without
007license or royalty fees, to use, copy, modify, and distribute this
008software and its documentation for any purpose, provided that the
009above copyright notice and the following two paragraphs appear in all
010copies of this software.
011
012IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016SUCH DAMAGE.
017
018THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023ENHANCEMENTS, OR MODIFICATIONS.
024
025                                                PT_COPYRIGHT_VERSION_2
026                                                COPYRIGHTENDKEY
027
028
029 */
030package ptolemy.vergil.basic.layout.kieler;
031
032import java.awt.geom.Point2D;
033import java.awt.geom.Rectangle2D;
034import java.io.File;
035import java.io.IOException;
036import java.util.Collections;
037import java.util.List;
038
039import javax.swing.SwingConstants;
040
041import org.eclipse.emf.common.util.URI;
042import org.eclipse.emf.ecore.resource.Resource;
043import org.eclipse.emf.ecore.resource.ResourceSet;
044import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
045import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
046
047import de.cau.cs.kieler.core.kgraph.KEdge;
048import de.cau.cs.kieler.core.kgraph.KNode;
049import de.cau.cs.kieler.core.kgraph.KPort;
050import de.cau.cs.kieler.core.math.KVector;
051import de.cau.cs.kieler.kiml.klayoutdata.KEdgeLayout;
052import de.cau.cs.kieler.kiml.klayoutdata.KPoint;
053import de.cau.cs.kieler.kiml.klayoutdata.KShapeLayout;
054import de.cau.cs.kieler.kiml.util.KimlUtil;
055
056///////////////////////////////////////////////////////////////////
057////KielerGraphUtil
058/**
059 * Static helper class to work with KIELER graph datas structures.
060 *
061 * @author Hauke Fuhrmann (<a href="mailto:haf@informatik.uni-kiel.de">haf</a>)
062 * @version $Id$
063 * @since Ptolemy II 8.0
064 * @Pt.ProposedRating Red (cxh)
065 * @Pt.AcceptedRating Red (cxh)
066 */
067public final class KielerGraphUtil {
068
069    ///////////////////////////////////////////////////////////////////
070    ////                         protected methods                 ////
071
072    /**
073     * Get the parent node of an KIELER KEdge. That is the KIELER KNode that
074     * graphically contains the edge. In particular that is the parent node of
075     * the source node of the edge. If the source node is null, then the result
076     * is also null.
077     *
078     * @param edge The KIELER edge to determine the parent node.
079     * @return The parent KIELER node of the given edge or null if the source
080     *         of the edge is undefined.
081     */
082    protected static KNode _getParent(KEdge edge) {
083        KNode source = edge.getSource();
084        if (source == null) {
085            return null;
086        }
087        if (KimlUtil.isDescendant(edge.getTarget(), source)) {
088            return source;
089        }
090        return source.getParent();
091    }
092
093    /**
094     * Get the upper left corner of the real bounding box of the contents of a
095     * given KIELER node. Calculate the minimal x and y coordinates of all
096     * nodes contained in the given node.
097     *
098     * @param parent The composite KIELER node that contains other nodes.
099     * @return The minimal x and y coordinates of all contained nodes. Might be
100     *         Float.MAX_VALUE, if the parent does not contain any children.
101     */
102    protected static KVector _getUpperLeftCorner(KNode parent) {
103        KVector v = new KVector(Double.MAX_VALUE, Double.MAX_VALUE);
104        for (KNode kNode : parent.getChildren()) {
105            KShapeLayout layout = kNode.getData(KShapeLayout.class);
106            if (layout.getXpos() < v.x) {
107                v.x = layout.getXpos();
108            }
109            if (layout.getYpos() < v.y) {
110                v.y = layout.getYpos();
111            }
112        }
113        return v;
114    }
115
116    /**
117     * Reposition a small object in a big object according to a given direction
118     * (NORTH, EAST, SOUTH, WEST). The small object will be aligned to the big
119     * object's direction side and centered on the other coordinate.
120     *
121     * @param originalBounds Big object's bounds
122     * @param shrunkBounds Small object's bounds
123     * @param direction Direction of the small object within the big object
124     *            given by a SwingConstants direction constant
125     * @param offset Offset of the lower bound of the port
126     * @return New location of the small object.
127     */
128    protected static Point2D _shrinkCoordinates(Rectangle2D originalBounds,
129            Rectangle2D shrunkBounds, int direction, float offset) {
130        double widthDiff = originalBounds.getWidth() - shrunkBounds.getWidth();
131        double heightDiff = originalBounds.getHeight()
132                - shrunkBounds.getHeight();
133        Point2D.Double location = new Point2D.Double();
134        switch (direction) {
135        case SwingConstants.NORTH:
136            location.x = originalBounds.getMinX() + widthDiff - offset;
137            location.y = originalBounds.getMinY();
138            break;
139        case SwingConstants.EAST:
140            location.x = originalBounds.getMaxX() - widthDiff;
141            location.y = originalBounds.getMinY() + offset;
142            break;
143        case SwingConstants.SOUTH:
144            location.x = originalBounds.getMinX() + offset;
145            location.y = originalBounds.getMaxY() - heightDiff;
146            break;
147        default:
148            location.x = originalBounds.getMinX();
149            location.y = originalBounds.getMinY() + heightDiff - offset;
150            break;
151        }
152        return location;
153    }
154
155    /**
156     * Debug output a KEdge to a String, i.e. will represent all bend points in
157     * the String.
158     *
159     * @param edge The edge to be toStringed
160     * @return A String representing the KEdge
161     */
162    protected static String _toString(KEdge edge) {
163        StringBuffer string = new StringBuffer();
164        string.append("[E:");
165        string.append("Source:" + (edge.getSource() == null ? "null"
166                : edge.getSource().hashCode()));
167        string.append(" Target:" + (edge.getSource() == null ? "null"
168                : edge.getTarget().hashCode()) + " Bends:");
169        KEdgeLayout layout = edge.getData(KEdgeLayout.class);
170        for (KPoint point : layout.getBendPoints()) {
171            string.append(point.getX() + "," + point.getY() + " ");
172        }
173
174        string.append("]");
175        return string.toString();
176    }
177
178    /**
179     * Debug output a KNode to a String, i.e. will represent the whole subgraph
180     * starting with this node recursively and also present all outgoing edges
181     * of all nodes.
182     *
183     * @param knode The node to be toStringed
184     * @return A String representing the KNode
185     */
186    protected static String _toString(KNode knode) {
187        return _toString(knode, 0);
188    }
189
190    /**
191     * Debug output a KNode to a String, i.e. will represent the whole subgraph
192     * starting with this node recursively and also present all outgoing edges
193     * of all nodes.
194     *
195     * @param knode The node to be toStringed
196     * @param level Tree level of the currently processed element. Used for
197     *            recursive operation.
198     * @return A String representing the KNode
199     */
200    protected static String _toString(KNode knode, int level) {
201        StringBuffer buffer = new StringBuffer();
202        KShapeLayout layout = knode.getData(KShapeLayout.class);
203        buffer.append("Node: X" + layout.getXpos() + ",Y" + layout.getYpos()
204                + ",W" + layout.getWidth() + ",H" + layout.getHeight()
205                + " Hash:" + knode.hashCode() + "\n");
206        List<KPort> ports = knode.getPorts();
207        for (KPort port : ports) {
208            buffer.append("      Port: " + port.hashCode() + "\n");
209        }
210        List<KEdge> edges = knode.getOutgoingEdges();
211        for (KEdge edge : edges) {
212            buffer.append(_toString(edge) + "\n");
213        }
214        List<KNode> children = knode.getChildren();
215        for (KNode node : children) {
216            buffer.append(+level + " " + _toString(node, level + 1));
217        }
218        return buffer.toString();
219    }
220
221    /**
222     * Write a KGraph (KIELER graph data structure) to a file in its XMI
223     * representation. Can be used for debugging (manually look at it) or
224     * loading it elsewhere, e.g. a KIELER Graph viewer. The default filename is
225     * kgraph.xmi and will be written to the current working directory.
226     *
227     * @param kgraph The KIELER graph data structure given by its root KNode.
228     */
229    protected static void _writeToFile(KNode kgraph) {
230        // Create a resource set.
231        ResourceSet resourceSet = new ResourceSetImpl();
232
233        // Register the default resource factory -- only needed for stand-alone!
234        resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
235                Resource.Factory.Registry.DEFAULT_EXTENSION,
236                new XMIResourceFactoryImpl());
237
238        try {
239            // Get the URI of the model file.
240            File file = new File("kgraph.xmi");
241            URI fileURI = URI.createFileURI(file.getAbsolutePath());
242
243            // Demand load the resource for this file.
244            Resource resource = resourceSet.createResource(fileURI);
245
246            if (resource == null) {
247                throw new NullPointerException(
248                        "Could not create a resource for \"" + fileURI + "\"");
249            } else {
250                resource.getContents().add(kgraph);
251
252                // Print the contents of the resource to System.out.
253                resource.save(Collections.EMPTY_MAP);
254            }
255        } catch (IOException e) {
256        }
257    }
258
259}