Coverage Report - th.co.edge.jseq.argouml.SequenceDiagram
 
Classes in this File Line Coverage Branch Coverage Complexity
SequenceDiagram
96%
78/81
71%
10/14
0
 
 1  
 /*
 2  
  * Copyright (c) 2003-2008, by Henrik Arro and Contributors
 3  
  *
 4  
  * This file is part of JSeq, a tool to automatically create
 5  
  * sequence diagrams by tracing program execution.
 6  
  *
 7  
  * See <http://jseq.sourceforge.net> for more information.
 8  
  *
 9  
  * JSeq is free software: you can redistribute it and/or modify
 10  
  * it under the terms of the GNU Lesser General Public License as
 11  
  * published by the Free Software Foundation, either version 3 of
 12  
  * the License, or (at your option) any later version.
 13  
  *
 14  
  * JSeq is distributed in the hope that it will be useful,
 15  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 17  
  * GNU Lesser General Public License for more details.
 18  
  *
 19  
  * You should have received a copy of the GNU Lesser General Public License
 20  
  * along with JSeq. If not, see <http://www.gnu.org/licenses/>.
 21  
  */
 22  
 
 23  
 package th.co.edge.jseq.argouml;
 24  
 
 25  
 import java.util.HashMap;
 26  
 import java.util.Iterator;
 27  
 import java.util.LinkedList;
 28  
 import java.util.List;
 29  
 import java.util.Map;
 30  
 
 31  
 import javax.xml.parsers.DocumentBuilder;
 32  
 import javax.xml.parsers.DocumentBuilderFactory;
 33  
 import javax.xml.parsers.ParserConfigurationException;
 34  
 
 35  
 import org.w3c.dom.DOMImplementation;
 36  
 import org.w3c.dom.Document;
 37  
 import org.w3c.dom.DocumentType;
 38  
 import org.w3c.dom.Element;
 39  
 
 40  
 import ru.novosoft.uml.behavior.common_behavior.MAction;
 41  
 import ru.novosoft.uml.behavior.common_behavior.MLink;
 42  
 import ru.novosoft.uml.behavior.common_behavior.MObject;
 43  
 import ru.novosoft.uml.behavior.common_behavior.MStimulus;
 44  
 import ru.novosoft.uml.foundation.core.MClass;
 45  
 import ru.novosoft.uml.foundation.core.MClassifier;
 46  
 import ru.novosoft.uml.foundation.core.MNamespace;
 47  
 
 48  
 import th.co.edge.jseq.Activation;
 49  
 import th.co.edge.jseq.MockObject;
 50  
 import th.co.edge.jseq.MockObjectMap;
 51  
 import th.co.edge.jseq.argouml.pgml.Fig;
 52  
 import th.co.edge.jseq.argouml.pgml.FigLink;
 53  
 import th.co.edge.jseq.argouml.pgml.FigObject;
 54  
 import th.co.edge.jseq.argouml.pgml.FigStimulus;
 55  
 
 56  
 /**
 57  
  * A <code>SequenceDiagram</code> represents a sequence diagram as a <a
 58  
  * href="http://en.wikipedia.org/wiki/PGML" target="new">PGML</a> document.
 59  
  */
 60  
 public class SequenceDiagram {
 61  1
     private static final String PGML_PUBLIC_ID = null;
 62  
     private static final String PGML_SYSTEM_ID = "pgml.dtd";
 63  1
     private static final String PGML_NAMESPACE = null;
 64  
 
 65  
     private static final String UML_SEQUENCE_DIAGRAM_CLASS =
 66  
             "org.argouml.uml.diagram.sequence.ui.UMLSequenceDiagram";
 67  
 
 68  
     private static final int TOP_MARGIN = 10;
 69  
     private static final int LEFT_MARGIN = 10;
 70  
     private static final int COLUMN_WIDTH = 135;
 71  
 
 72  
     private MNamespace namespace;
 73  
     private MockObjectMap mockObjectMap;
 74  
     private DocumentBuilder builder;
 75  
 
 76  1
     private Map<MObject, FigObject> figObjectMap =
 77  
             new HashMap<MObject, FigObject>();
 78  1
     private List<Fig> figObjects = new LinkedList<Fig>();
 79  1
     private List<Fig> figLinks = new LinkedList<Fig>();
 80  1
     private List<Fig> figStimuli = new LinkedList<Fig>();
 81  1
     private int nextFigNumber = 0;
 82  1
     private int nextPortNumber = 0;
 83  
 
 84  
     /**
 85  
      * Creates a new <code>SequenceDiagram</code> depicting a given root
 86  
      * activation.
 87  
      *
 88  
      * @param namespace
 89  
      *            only used to generate the description element in the PGML file
 90  
      * @param activation
 91  
      *            the root activation to depict as a sequence diagram
 92  
      *
 93  
      * @throws ParserConfigurationException
 94  
      *             if there is some serious error in the XML configuration
 95  
      *             (should normally not occur)
 96  
      */
 97  
     public SequenceDiagram(MNamespace namespace, Activation activation)
 98  1
             throws ParserConfigurationException {
 99  1
         this.namespace = namespace;
 100  1
         this.mockObjectMap = MockObjectMap.addAll(activation);
 101  
 
 102  1
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 103  1
         this.builder = factory.newDocumentBuilder();
 104  1
     }
 105  
 
 106  
     /**
 107  
      * Returns this sequence diagram as a PGML XML <code>Document</code>.
 108  
      *
 109  
      * @return this sequence diagram as a PGML XML <code>Document</code>
 110  
      */
 111  
     public Document getDocument() {
 112  1
         DOMImplementation impl = builder.getDOMImplementation();
 113  1
         DocumentType docType =
 114  
                 impl.createDocumentType("pgml", PGML_PUBLIC_ID, PGML_SYSTEM_ID);
 115  1
         Document doc = impl.createDocument(PGML_NAMESPACE, "pgml", docType);
 116  1
         Element root = doc.getDocumentElement();
 117  1
         root.setAttribute("description", getRootDescription());
 118  1
         addFigs(doc, figObjects);
 119  1
         addFigs(doc, figStimuli);
 120  1
         addFigs(doc, figLinks);
 121  1
         return doc;
 122  
     }
 123  
 
 124  
     private void addFigs(Document doc, List<Fig> figs) {
 125  3
         for (Fig fig : figs) {
 126  21
             doc.getDocumentElement().appendChild(fig.getXML(doc));
 127  
         }
 128  3
     }
 129  
 
 130  
     private String getRootDescription() {
 131  1
         return UML_SEQUENCE_DIAGRAM_CLASS + "|" + namespace.getUUID();
 132  
     }
 133  
 
 134  
     /**
 135  
      * Adds a life-line to this diagram, represented by an <code>MObject</code>.
 136  
      *
 137  
      * @param object
 138  
      *            the <code>MObject</code> to add
 139  
      */
 140  
     public void addObject(MObject object) {
 141  3
         FigObject figObject = createEmptyFigObject(object);
 142  3
         figObjects.add(figObject);
 143  3
         figObjectMap.put(object, figObject);
 144  3
     }
 145  
 
 146  
     /**
 147  
      * Adds an arrow representing a method call to this diagram, from a given
 148  
      * sender to a given receiver represented by activation boxes in the
 149  
      * resulting diagram.
 150  
      *
 151  
      * @param sender
 152  
      *            the <code>MObject</code> representing the caller
 153  
      *
 154  
      * @param receiver
 155  
      *            the <code>MObject</code> representing the callee
 156  
      *
 157  
      * @param link
 158  
      */
 159  
     @SuppressWarnings("unchecked")
 160  
     public void addCall(MObject sender, MObject receiver, MLink link) {
 161  9
         FigObject figSender = figObjectMap.get(sender);
 162  9
         FigObject figReceiver = figObjectMap.get(receiver);
 163  
 
 164  9
         String sourcePortFig = figSender.addPort(nextPortNumber);
 165  9
         if (!figSender.isActive()) {
 166  1
             activate(sender);
 167  
         }
 168  9
         String destPortFig = figReceiver.addPort(nextPortNumber);
 169  9
         activate(receiver);
 170  9
         nextPortNumber++;
 171  9
         String sourceFigNode = figSender.getName();
 172  9
         String destFigNode = figReceiver.getName();
 173  
 
 174  9
         for (Iterator i = link.getStimuli().iterator(); i.hasNext();) {
 175  9
             MStimulus stimulus = (MStimulus) i.next();
 176  9
             MAction action = stimulus.getDispatchAction();
 177  9
             FigStimulus figStimulus =
 178  
                     createFigStimulus(stimulus, action.getName());
 179  9
             figStimuli.add(figStimulus);
 180  9
         }
 181  9
         FigLink figLink =
 182  
                 createFigLink(link, sourcePortFig, destPortFig, sourceFigNode,
 183  
                         destFigNode);
 184  9
         figLinks.add(figLink);
 185  9
     }
 186  
 
 187  
     /**
 188  
      * Makes the given <code>MObject</code>, represented as a life-line in
 189  
      * the sequence diagram, active, that is, executing a method. This will be
 190  
      * represented as an activation box for the given <code>MObject</code>.
 191  
      *
 192  
      * @param object
 193  
      *            the <code>MObject</code> to activate
 194  
      */
 195  
     private void activate(MObject object) {
 196  10
         FigObject figObject = figObjectMap.get(object);
 197  10
         figObject.activate(nextPortNumber);
 198  10
     }
 199  
 
 200  
     /**
 201  
      * Deactivates the given <code>MObject</code>, that is, returns from a
 202  
      * method call. The activation box currently associated with this object
 203  
      * will be ended.
 204  
      *
 205  
      * @param object
 206  
      *            the <code>MObject</code> to deactivate
 207  
      */
 208  
     public void deactivate(MObject object) {
 209  10
         FigObject figObject = figObjectMap.get(object);
 210  10
         figObject.deactivate(nextPortNumber - 1);
 211  10
     }
 212  
 
 213  
     private FigObject createEmptyFigObject(MObject object) {
 214  3
         int column = getColumn(object);
 215  3
         FigObject fig =
 216  
                 new FigObject(getNextFigName(), getName(object), object
 217  
                         .getUUID(), LEFT_MARGIN, TOP_MARGIN + column *
 218  
                         COLUMN_WIDTH);
 219  3
         return fig;
 220  
     }
 221  
 
 222  
     private FigStimulus createFigStimulus(MStimulus stimulus, String name) {
 223  9
         FigStimulus fig =
 224  
                 new FigStimulus(getNextFigName(), name, stimulus.getUUID());
 225  9
         return fig;
 226  
     }
 227  
 
 228  
     private FigLink createFigLink(MLink link, String sourcePortFig,
 229  
             String destPortFig, String sourceFigNode, String destFigNode) {
 230  9
         FigLink fig =
 231  
                 new FigLink(getNextFigName(), link.getUUID(), LEFT_MARGIN,
 232  
                         TOP_MARGIN, sourcePortFig, destPortFig, sourceFigNode,
 233  
                         destFigNode);
 234  9
         return fig;
 235  
     }
 236  
 
 237  
     private int getColumn(MObject object) {
 238  3
         MClass cls = getClass(object);
 239  3
         MockObject mockObject = mockObjectMap.get(cls.getName());
 240  3
         if (mockObject == null) {
 241  0
             throw new IllegalArgumentException("Object not found " + object);
 242  
         }
 243  3
         return mockObject.getColumn();
 244  
     }
 245  
 
 246  
     private String getName(MObject object) {
 247  3
         MClass cls = getClass(object);
 248  3
         return object.getName() + " : " + cls.getName();
 249  
     }
 250  
 
 251  
     // TODO: Move SuppressWarnings annotations to inner-most scope possible.
 252  
     @SuppressWarnings("unchecked")
 253  
     private MClass getClass(MObject object) {
 254  6
         MClass cls = null;
 255  6
         for (Iterator i = object.getClassifiers().iterator(); i.hasNext();) {
 256  6
             MClassifier classifier = (MClassifier) i.next();
 257  6
             if (classifier instanceof MClass) {
 258  6
                 cls = (MClass) classifier;
 259  6
                 break;
 260  
             }
 261  0
         }
 262  6
         if (cls == null) {
 263  0
             throw new IllegalArgumentException("No class found for " + object);
 264  
         }
 265  6
         return cls;
 266  
     }
 267  
 
 268  
     private String getNextFigName() {
 269  21
         return "Fig" + nextFigNumber++;
 270  
     }
 271  
 }