Coverage Report - org.argouml.persistence.PGMLStackParser
 
Classes in this File Line Coverage Branch Coverage Complexity
PGMLStackParser
0%
0/242
0%
0/152
5.478
PGMLStackParser$EdgeData
0%
0/14
0%
0/8
5.478
 
 1  
 /* $Id: PGMLStackParser.java 18024 2010-02-16 21:25:10Z bobtarling $
 2  
  *****************************************************************************
 3  
  * Copyright (c) 2009 Contributors - see below
 4  
  * All rights reserved. This program and the accompanying materials
 5  
  * are made available under the terms of the Eclipse Public License v1.0
 6  
  * which accompanies this distribution, and is available at
 7  
  * http://www.eclipse.org/legal/epl-v10.html
 8  
  *
 9  
  * Contributors:
 10  
  *    Michiel Van Der Wulp
 11  
  *    Bob Tarling
 12  
  *    Thomas Neustupny
 13  
  *****************************************************************************
 14  
  *
 15  
  * Some portions of this file was previously release using the BSD License:
 16  
  */
 17  
 // Copyright (c) 2005-2009 The Regents of the University of California. All
 18  
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 19  
 // software and its documentation without fee, and without a written
 20  
 // agreement is hereby granted, provided that the above copyright notice
 21  
 // and this paragraph appear in all copies.  This software program and
 22  
 // documentation are copyrighted by The Regents of the University of
 23  
 // California. The software program and documentation are supplied "AS
 24  
 // IS", without any accompanying services from The Regents. The Regents
 25  
 // does not warrant that the operation of the program will be
 26  
 // uninterrupted or error-free. The end-user understands that the program
 27  
 // was developed for research purposes and is advised not to rely
 28  
 // exclusively on the program for any reason.  IN NO EVENT SHALL THE
 29  
 // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 30  
 // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 31  
 // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 32  
 // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 33  
 // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
 34  
 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 35  
 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 36  
 // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 37  
 // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
 38  
 // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 39  
 
 40  
 package org.argouml.persistence;
 41  
 
 42  
 import java.awt.Rectangle;
 43  
 import java.io.InputStream;
 44  
 import java.lang.reflect.Constructor;
 45  
 import java.lang.reflect.InvocationTargetException;
 46  
 import java.net.URL;
 47  
 import java.util.ArrayList;
 48  
 import java.util.HashMap;
 49  
 import java.util.LinkedHashMap;
 50  
 import java.util.List;
 51  
 import java.util.Map;
 52  
 import java.util.StringTokenizer;
 53  
 
 54  
 import org.apache.log4j.Logger;
 55  
 import org.argouml.model.Model;
 56  
 import org.argouml.uml.diagram.ArgoDiagram;
 57  
 import org.argouml.uml.diagram.DiagramEdgeSettings;
 58  
 import org.argouml.uml.diagram.DiagramSettings;
 59  
 import org.argouml.uml.diagram.PathContainer;
 60  
 import org.argouml.uml.diagram.StereotypeContainer;
 61  
 import org.argouml.uml.diagram.VisibilityContainer;
 62  
 import org.argouml.uml.diagram.ui.FigCompartmentBox;
 63  
 import org.argouml.uml.diagram.ui.FigEdgeModelElement;
 64  
 import org.argouml.uml.diagram.ui.FigEdgePort;
 65  
 import org.tigris.gef.base.Diagram;
 66  
 import org.tigris.gef.persistence.pgml.Container;
 67  
 import org.tigris.gef.persistence.pgml.FigEdgeHandler;
 68  
 import org.tigris.gef.persistence.pgml.FigGroupHandler;
 69  
 import org.tigris.gef.persistence.pgml.HandlerStack;
 70  
 import org.tigris.gef.presentation.Fig;
 71  
 import org.tigris.gef.presentation.FigEdge;
 72  
 import org.tigris.gef.presentation.FigGroup;
 73  
 import org.tigris.gef.presentation.FigNode;
 74  
 import org.xml.sax.Attributes;
 75  
 import org.xml.sax.InputSource;
 76  
 import org.xml.sax.SAXException;
 77  
 import org.xml.sax.helpers.DefaultHandler;
 78  
 
 79  
 // TODO: Move to Diagram subsystem?
 80  
 
 81  
 /**
 82  
  * The PGML Parser.
 83  
  * <p>
 84  
  * 
 85  
  * This replaces much of the identically named class from GEF.
 86  
  */
 87  
 class PGMLStackParser extends org.tigris.gef.persistence.pgml.PGMLStackParser {
 88  
 
 89  0
     private static final Logger LOG = Logger.getLogger(PGMLStackParser.class);
 90  
 
 91  0
     private List<EdgeData> figEdges = new ArrayList<EdgeData>(50);
 92  
 
 93  0
     private LinkedHashMap<FigEdge, Object> modelElementsByFigEdge = new LinkedHashMap<FigEdge, Object>(
 94  
             50);
 95  
 
 96  
     private DiagramSettings diagramSettings;
 97  
 
 98  
     // TODO: Use stylesheet to convert or wait till we use Fig
 99  
     // factories in diagram subsystem.
 100  
     // What is the last version that used FigNote?
 101  
     private void addTranslations() {
 102  0
         addTranslation("org.argouml.uml.diagram.ui.FigNote",
 103  
                 "org.argouml.uml.diagram.static_structure.ui.FigComment");
 104  0
         addTranslation("org.argouml.uml.diagram.static_structure.ui.FigNote",
 105  
                 "org.argouml.uml.diagram.static_structure.ui.FigComment");
 106  0
         addTranslation("org.argouml.uml.diagram.state.ui.FigState",
 107  
                 "org.argouml.uml.diagram.state.ui.FigSimpleState");
 108  0
         addTranslation("org.argouml.uml.diagram.ui.FigCommentPort",
 109  
                 "org.argouml.uml.diagram.ui.FigEdgePort");
 110  0
         addTranslation("org.tigris.gef.presentation.FigText",
 111  
                 "org.argouml.uml.diagram.ui.ArgoFigText");
 112  0
         addTranslation("org.tigris.gef.presentation.FigLine",
 113  
                 "org.argouml.gefext.ArgoFigLine");
 114  0
         addTranslation("org.tigris.gef.presentation.FigPoly",
 115  
                 "org.argouml.gefext.ArgoFigPoly");
 116  0
         addTranslation("org.tigris.gef.presentation.FigCircle",
 117  
                 "org.argouml.gefext.ArgoFigCircle");
 118  0
         addTranslation("org.tigris.gef.presentation.FigRect",
 119  
                 "org.argouml.gefext.ArgoFigRect");
 120  0
         addTranslation("org.tigris.gef.presentation.FigRRect",
 121  
                 "org.argouml.gefext.ArgoFigRRect");
 122  0
         addTranslation(
 123  
                 "org.argouml.uml.diagram.deployment.ui.FigMNodeInstance",
 124  
                 "org.argouml.uml.diagram.deployment.ui.FigNodeInstance");
 125  0
         addTranslation("org.argouml.uml.diagram.ui.FigRealization",
 126  
                 "org.argouml.uml.diagram.ui.FigAbstraction");
 127  0
     }
 128  
 
 129  
     /**
 130  
      * Construct a PGML parser with the given HREF/Object map and default
 131  
      * diagram settings.
 132  
      * 
 133  
      * @param modelElementsByUuid map of HREF ids to objects used to associate
 134  
      *            Figs with their owning model elements
 135  
      * @param defaultSettings default diagram settings to use for newly created
 136  
      *            diagram and its contained Figs
 137  
      */
 138  
     public PGMLStackParser(Map<String, Object> modelElementsByUuid,
 139  
             DiagramSettings defaultSettings) {
 140  0
         super(modelElementsByUuid);
 141  0
         addTranslations();
 142  
         // Create a new diagram wide settings block which is backed by
 143  
         // the project-wide defaults that we were passed
 144  0
         diagramSettings = new DiagramSettings(defaultSettings);
 145  0
     }
 146  
 
 147  
     /*
 148  
      * @see org.tigris.gef.persistence.pgml.HandlerFactory#getHandler(
 149  
      * HandlerStack, Object, String, String, String, Attributes)
 150  
      */
 151  
     @Override
 152  
     public DefaultHandler getHandler(HandlerStack stack, Object container,
 153  
             String uri, String localname, String qname, Attributes attributes)
 154  
         throws SAXException {
 155  
 
 156  0
         String href = attributes.getValue("href");
 157  0
         Object owner = null;
 158  
 
 159  0
         if (href != null) {
 160  0
             owner = findOwner(href);
 161  0
             if (owner == null) {
 162  0
                 LOG.warn("Found href of " + href
 163  
                         + " with no matching element in model");
 164  0
                 return null;
 165  
             }
 166  
         }
 167  
 
 168  
         // Ignore non-private elements within FigNode groups
 169  0
         if (container instanceof FigGroupHandler) {
 170  0
             FigGroup group = ((FigGroupHandler) container).getFigGroup();
 171  0
             if (group instanceof FigNode && !qname.equals("private")) {
 172  0
                 return null;
 173  
             }
 174  
         }
 175  
 
 176  
         // Handle ItemUID in container contents
 177  0
         if (qname.equals("private") && (container instanceof Container)) {
 178  0
             return new PrivateHandler(this, (Container) container);
 179  
         }
 180  
 
 181  0
         DefaultHandler handler = super.getHandler(stack, container, uri,
 182  
                 localname, qname, attributes);
 183  
 
 184  0
         if (handler instanceof FigEdgeHandler) {
 185  0
             return new org.argouml.persistence.FigEdgeHandler(this,
 186  
                     ((FigEdgeHandler) handler).getFigEdge());
 187  
         }
 188  
 
 189  0
         return handler;
 190  
 
 191  
     }
 192  
 
 193  
     /*
 194  
      * @see org.tigris.gef.persistence.pgml.PGMLStackParser#setAttrs(
 195  
      * org.tigris.gef.presentation.Fig, org.xml.sax.Attributes)
 196  
      */
 197  
     @Override
 198  
     protected final void setAttrs(Fig f, Attributes attrList)
 199  
         throws SAXException {
 200  
 
 201  0
         if (f instanceof FigGroup) {
 202  0
             FigGroup group = (FigGroup) f;
 203  0
             String clsNameBounds = attrList.getValue("description");
 204  0
             if (clsNameBounds != null) {
 205  0
                 StringTokenizer st = new StringTokenizer(clsNameBounds, ",;[] ");
 206  
                 // Discard class name, x y w h
 207  0
                 if (st.hasMoreElements()) {
 208  0
                     st.nextToken();
 209  
                 }
 210  0
                 if (st.hasMoreElements()) {
 211  0
                     st.nextToken();
 212  
                 }
 213  0
                 if (st.hasMoreElements()) {
 214  0
                     st.nextToken();
 215  
                 }
 216  0
                 if (st.hasMoreElements()) {
 217  0
                     st.nextToken();
 218  
                 }
 219  0
                 if (st.hasMoreElements()) {
 220  0
                     st.nextToken();
 221  
                 }
 222  
 
 223  0
                 Map<String, String> attributeMap = interpretStyle(st);
 224  0
                 setStyleAttributes(group, attributeMap);
 225  
             }
 226  
         }
 227  
 
 228  
         // TODO: Attempt to move the following code to GEF
 229  
 
 230  0
         String name = attrList.getValue("name");
 231  0
         if (name != null && !name.equals("")) {
 232  0
             registerFig(f, name);
 233  
         }
 234  
 
 235  0
         setCommonAttrs(f, attrList);
 236  
 
 237  0
         final String href = attrList.getValue("href");
 238  0
         if (href != null && !href.equals("")) {
 239  0
             Object modelElement = findOwner(href);
 240  0
             if (modelElement == null) {
 241  0
                 LOG.error("Can't find href of " + href);
 242  0
                 throw new SAXException("Found href of " + href
 243  
                         + " with no matching element in model");
 244  
             }
 245  
             // The owner should always have already been set in the constructor
 246  0
             if (f.getOwner() != modelElement) {
 247  
                 // Assign nodes immediately but edges later. See issue 4310.
 248  0
                 if (f instanceof FigEdge) {
 249  0
                     modelElementsByFigEdge.put((FigEdge) f, modelElement);
 250  
                 } else {
 251  0
                     f.setOwner(modelElement);
 252  
                 }
 253  
             } else {
 254  0
                 LOG.debug("Ignoring href on " + f.getClass().getName()
 255  
                         + " as it's already set");
 256  
             }
 257  
         }
 258  0
     }
 259  
 
 260  
     /**
 261  
      * The StringTokenizer is expected to be positioned at the start of a string
 262  
      * of style identifiers in the format name=value;name=value;name=value....
 263  
      * Each name value pair is interpreted and the Fig configured accordingly.
 264  
      * The value is optional and will default to a value applicable for its
 265  
      * name. The current applicable names are operationsVisible and
 266  
      * attributesVisible and are used to show or hide the compartments within
 267  
      * Class and Interface. The default values are true.
 268  
      * 
 269  
      * @param st The StrinkTokenizer positioned at the first style identifier
 270  
      * @return a map of attributes
 271  
      */
 272  
     private Map<String, String> interpretStyle(StringTokenizer st) {
 273  0
         Map<String, String> map = new HashMap<String, String>();
 274  
         String name;
 275  
         String value;
 276  
 
 277  0
         while (st.hasMoreElements()) {
 278  0
             String namevaluepair = st.nextToken();
 279  0
             int equalsPos = namevaluepair.indexOf('=');
 280  0
             if (equalsPos < 0) {
 281  0
                 name = namevaluepair;
 282  0
                 value = "true";
 283  
             } else {
 284  0
                 name = namevaluepair.substring(0, equalsPos);
 285  0
                 value = namevaluepair.substring(equalsPos + 1);
 286  
             }
 287  
 
 288  0
             map.put(name, value);
 289  0
         }
 290  0
         return map;
 291  
     }
 292  
 
 293  
     /**
 294  
      * Set the fig style attributes.
 295  
      * <p>
 296  
      * 
 297  
      * TODO: This should move into the render factories as described in issue
 298  
      * 859.
 299  
      * 
 300  
      * @param fig the fig to style.
 301  
      * @param attributeMap a map of name value pairs
 302  
      */
 303  
     private void setStyleAttributes(Fig fig, Map<String, String> attributeMap) {
 304  
 
 305  0
         for (Map.Entry<String, String> entry : attributeMap.entrySet()) {
 306  0
             final String name = entry.getKey();
 307  0
             final String value = entry.getValue();
 308  
 
 309  0
             if (fig instanceof FigCompartmentBox) {
 310  0
                 FigCompartmentBox fcb = (FigCompartmentBox) fig;
 311  0
                 if ("operationsVisible".equals(name)) {
 312  0
                     fcb.showCompartment(Model.getMetaTypes().getOperation(),
 313  
                             value.equalsIgnoreCase("true"));
 314  0
                 } else if ("attributesVisible".equals(name)) {
 315  0
                     fcb.showCompartment(Model.getMetaTypes().getAttribute(),
 316  
                             value.equalsIgnoreCase("true"));
 317  0
                 } else if ("enumerationLiteralsVisible".equals(name)) {
 318  0
                     fcb.showCompartment(Model.getMetaTypes()
 319  
                             .getEnumerationLiteral(), value
 320  
                             .equalsIgnoreCase("true"));
 321  0
                 } else if ("extensionPointVisible".equals(name)) {
 322  0
                     fcb.showCompartment(Model.getMetaTypes()
 323  
                             .getExtensionPoint(), value
 324  
                             .equalsIgnoreCase("true"));
 325  
                 }
 326  
             }
 327  0
             if ("stereotypeVisible".equals(name)) {
 328  0
                 ((StereotypeContainer) fig).setStereotypeVisible(value
 329  
                         .equalsIgnoreCase("true"));
 330  0
             } else if ("visibilityVisible".equals(name)) {
 331  0
                 ((VisibilityContainer) fig).setVisibilityVisible(value
 332  
                         .equalsIgnoreCase("true"));
 333  0
             } else if ("pathVisible".equals(name)) {
 334  0
                 ((PathContainer) fig).setPathVisible(value
 335  
                         .equalsIgnoreCase("true"));
 336  
             }
 337  0
         }
 338  0
     }
 339  
 
 340  
     /**
 341  
      * Read and parse the input stream to create a new diagram and return it.
 342  
      * 
 343  
      * @param is the input stream
 344  
      * @param closeStream true to close the stream when parsing is complete
 345  
      * @return the diagram created as a result of the parse
 346  
      * @throws SAXException
 347  
      */
 348  
     public ArgoDiagram readArgoDiagram(InputSource is, boolean closeStream)
 349  
         throws SAXException {
 350  
 
 351  0
         InputStream stream = is.getByteStream();
 352  0
         if (stream == null) {
 353  
             try {
 354  
                 // happens when 'is' comes from a zip file
 355  0
                 URL url = new URL(is.getSystemId());
 356  0
                 stream = url.openStream();
 357  0
                 closeStream = true;
 358  0
             } catch (Exception e) {
 359  
                 // continue with null stream, readDiagram(...) will take care of
 360  
                 // it
 361  0
             }
 362  
         }
 363  0
         return (ArgoDiagram) readDiagram(stream, closeStream);
 364  
     }
 365  
 
 366  
     /**
 367  
      * Read and parse the input stream to create a new diagram and return it.
 368  
      * 
 369  
      * @param is the input stream
 370  
      * @param closeStream true to close the stream when parsing is complete
 371  
      * @return the diagram created as a result of the parse
 372  
      * @throws SAXException
 373  
      */
 374  
     public ArgoDiagram readArgoDiagram(InputStream is, boolean closeStream)
 375  
         throws SAXException {
 376  
 
 377  0
         return (ArgoDiagram) readDiagram(is, closeStream);
 378  
     }
 379  
 
 380  
     @Override
 381  
     public Diagram readDiagram(InputStream is, boolean closeStream)
 382  
         throws SAXException {
 383  
 
 384  
         // TODO: we really want to be able replace the initial content handler
 385  
         // which is passed to SAX, but we can't do this without cloning a
 386  
         // whole bunch of code because it's private in the super class.
 387  
 
 388  0
         Diagram d = super.readDiagram(is, closeStream);
 389  
 
 390  0
         attachEdges(d);
 391  
 
 392  0
         return d;
 393  
     }
 394  
 
 395  
     /**
 396  
      * This is called when all nodes and edges have been read and placed on the
 397  
      * diagram. This method then attaches the edges to the correct node,
 398  
      * including the nodes contained within edges allowing edge to edge
 399  
      * connections for comment edges, association classes and dependencies.
 400  
      * 
 401  
      * @param d the Diagram
 402  
      */
 403  
     private void attachEdges(Diagram d) {
 404  0
         for (EdgeData edgeData : figEdges) {
 405  0
             final FigEdge edge = edgeData.getFigEdge();
 406  
 
 407  0
             Object modelElement = modelElementsByFigEdge.get(edge);
 408  0
             if (modelElement != null) {
 409  0
                 if (edge.getOwner() == null) {
 410  0
                     edge.setOwner(modelElement);
 411  
                 }
 412  
             }
 413  0
         }
 414  
 
 415  0
         for (EdgeData edgeData : figEdges) {
 416  0
             final FigEdge edge = edgeData.getFigEdge();
 417  
 
 418  0
             Fig sourcePortFig = findFig(edgeData.getSourcePortFigId());
 419  0
             Fig destPortFig = findFig(edgeData.getDestPortFigId());
 420  0
             final FigNode sourceFigNode = getFigNode(edgeData
 421  
                     .getSourceFigNodeId());
 422  0
             final FigNode destFigNode = getFigNode(edgeData.getDestFigNodeId());
 423  
 
 424  0
             if (sourceFigNode instanceof FigEdgePort) {
 425  0
                 sourcePortFig = sourceFigNode;
 426  
             }
 427  
 
 428  0
             if (destFigNode instanceof FigEdgePort) {
 429  0
                 destPortFig = destFigNode;
 430  
             }
 431  
 
 432  0
             if (sourcePortFig == null && sourceFigNode != null) {
 433  0
                 sourcePortFig = getPortFig(sourceFigNode);
 434  
             }
 435  
 
 436  0
             if (destPortFig == null && destFigNode != null) {
 437  0
                 destPortFig = getPortFig(destFigNode);
 438  
             }
 439  
 
 440  0
             if (sourcePortFig == null || destPortFig == null
 441  
                     || sourceFigNode == null || destFigNode == null) {
 442  0
                 LOG.error("Can't find nodes for FigEdge: " + edge.getId() + ":"
 443  
                         + edge.toString());
 444  0
                 edge.removeFromDiagram();
 445  
             } else {
 446  0
                 edge.setSourcePortFig(sourcePortFig);
 447  0
                 edge.setDestPortFig(destPortFig);
 448  0
                 edge.setSourceFigNode(sourceFigNode);
 449  0
                 edge.setDestFigNode(destFigNode);
 450  
             }
 451  0
         }
 452  
 
 453  
         // Once all edges are connected do a compute route on each to make
 454  
         // sure that annotations and the edge port is positioned correctly
 455  
         // Only do this after all edges are connected as compute route
 456  
         // requires all edges to be connected to nodes.
 457  
         // TODO: It would be nice not to have to do this and restore annotation
 458  
         // positions instead.
 459  0
         for (Object edge : d.getLayer().getContentsEdgesOnly()) {
 460  0
             FigEdge figEdge = (FigEdge) edge;
 461  0
             figEdge.computeRouteImpl();
 462  0
         }
 463  0
     }
 464  
 
 465  
     // TODO: Move to GEF
 466  
     /**
 467  
      * Store data of a FigEdge together with the id's of nodes to connect to
 468  
      * 
 469  
      * @param figEdge The FigEdge
 470  
      * @param sourcePortFigId The id of the source port
 471  
      * @param destPortFigId The id of the destination port
 472  
      * @param sourceFigNodeId The id of the source node
 473  
      * @param destFigNodeId The id of the destination node
 474  
      */
 475  
     public void addFigEdge(final FigEdge figEdge, final String sourcePortFigId,
 476  
             final String destPortFigId, final String sourceFigNodeId,
 477  
             final String destFigNodeId) {
 478  0
         figEdges.add(new EdgeData(figEdge, sourcePortFigId, destPortFigId,
 479  
                 sourceFigNodeId, destFigNodeId));
 480  0
     }
 481  
 
 482  
     // TODO: Move to GEF
 483  
     /**
 484  
      * Get the FigNode that the fig id represents.
 485  
      * 
 486  
      * @param figId (In the form Figx.y.z)
 487  
      * @return the FigNode with the given id
 488  
      * @throws IllegalStateException if the figId supplied is not of a FigNode
 489  
      */
 490  
     private FigNode getFigNode(String figId) throws IllegalStateException {
 491  0
         if (figId.contains(".")) {
 492  
             // If the id does not look like a top-level Fig then we can assume
 493  
             // that this is an id of a FigEdgePort inside some FigEdge.
 494  
             // So extract the FigEdgePort from the FigEdge and return that as
 495  
             // the FigNode.
 496  0
             figId = figId.substring(0, figId.indexOf('.'));
 497  0
             FigEdgeModelElement edge = (FigEdgeModelElement) findFig(figId);
 498  0
             if (edge == null) {
 499  0
                 throw new IllegalStateException("Can't find a FigNode with id "
 500  
                         + figId);
 501  
             }
 502  0
             edge.makeEdgePort();
 503  0
             return edge.getEdgePort();
 504  
         } else {
 505  
             // If there is no dot then this must be a top level Fig and can be
 506  
             // assumed to be a FigNode.
 507  0
             Fig f = findFig(figId);
 508  0
             if (f instanceof FigNode) {
 509  0
                 return (FigNode) f;
 510  
             } else {
 511  0
                 LOG.error("FigID " + figId + " is not a node, edge ignored");
 512  0
                 return null;
 513  
             }
 514  
         }
 515  
     }
 516  
 
 517  
     // TODO: Move to GEF
 518  
     /**
 519  
      * Get the Fig from the FigNode that is the port.
 520  
      * 
 521  
      * @param figNode the FigNode
 522  
      * @return the Fig that is the port on the given FigNode
 523  
      */
 524  
     private Fig getPortFig(FigNode figNode) {
 525  0
         if (figNode instanceof FigEdgePort) {
 526  
             // TODO: Can we just do this every time, no need for else - Bob
 527  0
             return figNode;
 528  
         } else {
 529  0
             return (Fig) figNode.getPortFigs().get(0);
 530  
         }
 531  
     }
 532  
 
 533  
     // TODO: Move to GEF
 534  
 
 535  
     /**
 536  
      * The data from an edge extracted from the PGML before we can guarantee all
 537  
      * the nodes have been constructed. This stores the FigEdge and the id's of
 538  
      * the nodes to connect to later. If the nodes are not known then the ports
 539  
      * are returned instead.
 540  
      */
 541  
     private class EdgeData {
 542  
         private final FigEdge figEdge;
 543  
 
 544  
         private final String sourcePortFigId;
 545  
 
 546  
         private final String destPortFigId;
 547  
 
 548  
         private final String sourceFigNodeId;
 549  
 
 550  
         private final String destFigNodeId;
 551  
 
 552  
         /**
 553  
          * Constructor
 554  
          * 
 555  
          * @param edge The FigEdge
 556  
          * @param sourcePortId The id of the source port
 557  
          * @param destPortId The id of the destination port
 558  
          * @param sourceNodeId The id of the source node
 559  
          * @param destNodeId The id of the destination node
 560  
          */
 561  
         public EdgeData(FigEdge edge, String sourcePortId, String destPortId,
 562  0
                 String sourceNodeId, String destNodeId) {
 563  0
             if (sourcePortId == null || destPortId == null) {
 564  0
                 throw new IllegalArgumentException(
 565  
                         "source port and dest port must not be null"
 566  
                                 + " source = " + sourcePortId + " dest = "
 567  
                                 + destPortId + " figEdge = " + edge);
 568  
             }
 569  0
             this.figEdge = edge;
 570  0
             this.sourcePortFigId = sourcePortId;
 571  0
             this.destPortFigId = destPortId;
 572  0
             this.sourceFigNodeId = sourceNodeId != null ? sourceNodeId
 573  
                     : sourcePortId;
 574  0
             this.destFigNodeId = destNodeId != null ? destNodeId : destPortId;
 575  0
         }
 576  
 
 577  
         /**
 578  
          * Get the id of the destination FigNode
 579  
          * 
 580  
          * @return the id
 581  
          */
 582  
         public String getDestFigNodeId() {
 583  0
             return destFigNodeId;
 584  
         }
 585  
 
 586  
         /**
 587  
          * Get the id of the destination port
 588  
          * 
 589  
          * @return the id
 590  
          */
 591  
         public String getDestPortFigId() {
 592  0
             return destPortFigId;
 593  
         }
 594  
 
 595  
         /**
 596  
          * Get the FigEdge
 597  
          * 
 598  
          * @return the FigEdge
 599  
          */
 600  
         public FigEdge getFigEdge() {
 601  0
             return figEdge;
 602  
         }
 603  
 
 604  
         /**
 605  
          * Get the id of the source FigNode
 606  
          * 
 607  
          * @return the id
 608  
          */
 609  
         public String getSourceFigNodeId() {
 610  0
             return sourceFigNodeId;
 611  
         }
 612  
 
 613  
         /**
 614  
          * Get the id of the source port
 615  
          * 
 616  
          * @return the id
 617  
          */
 618  
         public String getSourcePortFigId() {
 619  0
             return sourcePortFigId;
 620  
         }
 621  
     }
 622  
 
 623  
     /**
 624  
      * Construct a new instance of the named Fig with the owner represented by
 625  
      * the given href and the bounds parsed from the PGML file. We look for
 626  
      * constructors of the form Fig(Object owner, Rectangle bounds,
 627  
      * DiagramSettings settings) which is typically used for subclasses of
 628  
      * FigNodeModelElement, then Fig(Object owner, DiagramSettings settings)
 629  
      * which is used for subclasses of FigEdgeModelElement.
 630  
      * <p>
 631  
      * If we fail to find any of the constructors that we know about, we'll call
 632  
      * GEF's version of this method to see if it can find a constructor.
 633  
      * 
 634  
      * @param className fully qualified name of class to instantiate
 635  
      * @param href string representing UUID of owning element
 636  
      * @param bounds position and size of figure
 637  
      * @return
 638  
      * @throws SAXException
 639  
      * @see org.tigris.gef.persistence.pgml.PGMLStackParser#constructFig(java.lang.String,
 640  
      *      java.lang.String, java.awt.Rectangle)
 641  
      */
 642  
     @Override
 643  
     protected Fig constructFig(final String className, final String href,
 644  
             final Rectangle bounds, final Attributes attributes)
 645  
         throws SAXException {
 646  
 
 647  0
         final DiagramSettings diagramSettings = ((ArgoDiagram) getDiagram())
 648  
                 .getDiagramSettings();
 649  
 
 650  0
         Fig f = null;
 651  
         try {
 652  0
             Class figClass = Class.forName(className);
 653  
 
 654  0
             final Constructor[] constructors = figClass.getConstructors();
 655  
 
 656  
             // We are looking first to match with 3 different constructor
 657  
             // types. We would not expect a Fig to have any mix of these.
 658  
             // Any constructor other than these should be deprecated so we
 659  
             // look for these first.
 660  
             // Fig(DiagramEdgeSettings, DiagramSettings)
 661  
             // Fig(Object, Rectangle, DiagramSettings)
 662  
             // Fig(Rectangle, DiagramSettings)
 663  0
             for (Constructor constructor : constructors) {
 664  0
                 Class[] parameterTypes = constructor.getParameterTypes();
 665  0
                 if (parameterTypes.length == 3
 666  
                         && parameterTypes[0].equals(Object.class)
 667  
                         && parameterTypes[1].equals(Rectangle.class)
 668  
                         && parameterTypes[2].equals(DiagramSettings.class)) {
 669  
                     // FigNodeModelElements should match here
 670  0
                     final Object parameters[] = new Object[3];
 671  0
                     final Object owner = getOwner(className, href);
 672  0
                     if (owner == null) {
 673  0
                         return null;
 674  
                     }
 675  0
                     parameters[0] = owner;
 676  0
                     parameters[1] = bounds;
 677  0
                     parameters[2] = diagramSettings;
 678  
 
 679  0
                     constructor.setAccessible(true);
 680  0
                     f = (Fig) constructor.newInstance(parameters);
 681  0
                 } else if (parameterTypes.length == 2
 682  
                         && parameterTypes[0].equals(DiagramEdgeSettings.class)
 683  
                         && parameterTypes[1].equals(DiagramSettings.class)) {
 684  
                     // FigEdgeModelElements should match here (they have no
 685  
                     // bounds)
 686  0
                     final Object parameters[] = new Object[2];
 687  0
                     final Object owner = getOwner(className, href);
 688  0
                     if (owner == null) {
 689  0
                         return null;
 690  
                     }
 691  
 
 692  0
                     String sourceUuid = attributes.getValue("sourceConnector");
 693  0
                     String destinationUuid = attributes
 694  
                             .getValue("destConnector");
 695  
 
 696  
                     final Object source;
 697  
                     final Object destination;
 698  0
                     if (sourceUuid != null && destinationUuid != null) {
 699  0
                         source = findOwner(sourceUuid);
 700  0
                         destination = findOwner(destinationUuid);
 701  
                     } else {
 702  0
                         source = null;
 703  0
                         destination = null;
 704  
                     }
 705  
 
 706  0
                     DiagramEdgeSettings settings = new DiagramEdgeSettings(
 707  
                             owner, source, destination);
 708  0
                     parameters[0] = settings;
 709  0
                     parameters[1] = diagramSettings;
 710  
 
 711  0
                     constructor.setAccessible(true);
 712  0
                     f = (Fig) constructor.newInstance(parameters);
 713  0
                 } else if (parameterTypes.length == 2
 714  
                         && parameterTypes[0].equals(Rectangle.class)
 715  
                         && parameterTypes[1].equals(DiagramSettings.class)) {
 716  
                     // A FigNodeModelElement with no owner should match here
 717  
                     // TODO: This is a temporary solution due to FigPool
 718  
                     // extending
 719  
                     // FigNodeModelElement when in fact it should not do so.
 720  0
                     Object parameters[] = new Object[2];
 721  0
                     parameters[0] = bounds;
 722  0
                     parameters[1] = diagramSettings;
 723  
 
 724  0
                     constructor.setAccessible(true);
 725  0
                     f = (Fig) constructor.newInstance(parameters);
 726  
                 }
 727  
             }
 728  0
             if (f == null) {
 729  
                 // If no Fig was created by the code above then we must go
 730  
                 // look for the old style constructor that should have fallen
 731  
                 // into disuse by now.
 732  
                 // Fig(Object, Rectangle, DiagramSettings)
 733  
                 // All of these constructors should have been deprecated
 734  
                 // at least and replaced with the new signature. This is
 735  
                 // here for paranoia only until all Figs have been reviewed.
 736  0
                 for (Constructor constructor : constructors) {
 737  0
                     Class[] parameterTypes = constructor.getParameterTypes();
 738  0
                     if (parameterTypes.length == 2
 739  
                             && parameterTypes[0].equals(Object.class)
 740  
                             && parameterTypes[1].equals(DiagramSettings.class)) {
 741  0
                         Object parameters[] = new Object[2];
 742  
 
 743  0
                         final Object owner = getOwner(className, href);
 744  
                         // currently FigEdgeNote can be passed null
 745  
 //                        if (owner == null) {
 746  
 //                            return null;
 747  
 //                        }
 748  0
                         parameters[0] = owner;
 749  0
                         parameters[1] = diagramSettings;
 750  
 
 751  0
                         constructor.setAccessible(true);
 752  0
                         f = (Fig) constructor.newInstance(parameters);
 753  0
                         LOG.warn("Fig created by old style constructor "
 754  
                                 + f.getClass().getName());
 755  0
                         break;
 756  
                     }
 757  
                 }
 758  
             }
 759  0
         } catch (ClassNotFoundException e) {
 760  0
             throw new SAXException(e);
 761  0
         } catch (IllegalAccessException e) {
 762  0
             throw new SAXException(e);
 763  0
         } catch (InstantiationException e) {
 764  0
             throw new SAXException(e);
 765  0
         } catch (InvocationTargetException e) {
 766  0
             throw new SAXException(e);
 767  0
         }
 768  
 
 769  
         // Fall back to GEF's handling if we couldn't find an appropriate
 770  
         // constructor
 771  0
         if (f == null) {
 772  0
             LOG.warn("No ArgoUML constructor found for " + className
 773  
                     + " falling back to GEF's default constructors");
 774  0
             f = super.constructFig(className, href, bounds, attributes);
 775  
         }
 776  
 
 777  0
         return f;
 778  
     }
 779  
 
 780  
     /**
 781  
      * Given the href extracted from the PGML return the model element with that
 782  
      * uuid.
 783  
      * 
 784  
      * @param className Used only for logging should the href not be found
 785  
      * @param href The href
 786  
      * @return
 787  
      */
 788  
     private Object getOwner(String className, String id) {
 789  0
         if (id == null) {
 790  0
             LOG.warn("There is no href attribute provided for a " + className
 791  
                     + " so the diagram element is ignored on load");
 792  0
             return null;
 793  
         }
 794  0
         final Object owner = findOwner(id);
 795  0
         if (owner == null) {
 796  0
             LOG.warn("The href " + id + " is not found for a " + className
 797  
                     + " so the diagram element is ignored on load");
 798  0
             return null;
 799  
         }
 800  0
         return owner;
 801  
     }
 802  
 
 803  
     /**
 804  
      * Save the newly created Diagram for use by the parser. We take the
 805  
      * opportunity to attach our default diagram settings to it so we'll have
 806  
      * them if needed when constructing Figs.
 807  
      * <p>
 808  
      * Diagrams are created in GEF's PGMLHandler.initDiagram() which is private
 809  
      * and can't be overridden. Initialization sequence is:
 810  
      * <ul>
 811  
      * <li>load diagram class using name in PGML file
 812  
      * <li>instantiate using 0-arg constructor
 813  
      * <li>invoke this method (setDiagram(<newDiagramInstance))
 814  
      * <li>invoke diagram's initialize(Object owner) method
 815  
      * <li>diagram.setName()
 816  
      * <li>diagram.setScale()
 817  
      * <li>diagram.setShowSingleMultiplicity() (?!why does GEF care about
 818  
      * multiplicity?!)
 819  
      * </ul>
 820  
      * 
 821  
      * @param diagram the new diagram
 822  
      * @see org.tigris.gef.persistence.pgml.PGMLStackParser#setDiagram(org.tigris.gef.base.Diagram)
 823  
      */
 824  
     @Override
 825  
     public void setDiagram(Diagram diagram) {
 826  
         // TODO: We could generalize this to initialize more stuff if needed
 827  0
         ((ArgoDiagram) diagram).setDiagramSettings(getDiagramSettings());
 828  0
         super.setDiagram(diagram);
 829  0
     }
 830  
 
 831  
     public DiagramSettings getDiagramSettings() {
 832  0
         return diagramSettings;
 833  
     }
 834  
 }