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}