Coverage Report - org.argouml.notation.providers.uml.TransitionNotationUml
 
Classes in this File Line Coverage Branch Coverage Complexity
TransitionNotationUml
0%
0/284
0%
0/208
6.524
 
 1  
 /* $Id: TransitionNotationUml.java 18852 2010-11-20 19:27:11Z mvw $
 2  
  *****************************************************************************
 3  
  * Copyright (c) 2009-2010 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  
  *****************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 2005-2009 The Regents of the University of California. All
 17  
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 18  
 // software and its documentation without fee, and without a written
 19  
 // agreement is hereby granted, provided that the above copyright notice
 20  
 // and this paragraph appear in all copies.  This software program and
 21  
 // documentation are copyrighted by The Regents of the University of
 22  
 // California. The software program and documentation are supplied "AS
 23  
 // IS", without any accompanying services from The Regents. The Regents
 24  
 // does not warrant that the operation of the program will be
 25  
 // uninterrupted or error-free. The end-user understands that the program
 26  
 // was developed for research purposes and is advised not to rely
 27  
 // exclusively on the program for any reason.  IN NO EVENT SHALL THE
 28  
 // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 29  
 // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 30  
 // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 31  
 // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 32  
 // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
 33  
 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 34  
 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 35  
 // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 36  
 // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
 37  
 // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 38  
 
 39  
 package org.argouml.notation.providers.uml;
 40  
 
 41  
 import java.text.ParseException;
 42  
 import java.util.Collection;
 43  
 import java.util.Iterator;
 44  
 import java.util.StringTokenizer;
 45  
 
 46  
 import org.argouml.application.events.ArgoEventPump;
 47  
 import org.argouml.application.events.ArgoEventTypes;
 48  
 import org.argouml.application.events.ArgoHelpEvent;
 49  
 import org.argouml.i18n.Translator;
 50  
 import org.argouml.model.Model;
 51  
 import org.argouml.model.StateMachinesFactory;
 52  
 import org.argouml.notation.NotationSettings;
 53  
 import org.argouml.notation.providers.TransitionNotation;
 54  
 
 55  
 /**
 56  
  * UML Notation for the text shown next to a Transition.
 57  
  *
 58  
  * @author Michiel van der Wulp
 59  
  */
 60  
 public class TransitionNotationUml extends TransitionNotation {
 61  
 
 62  
     /**
 63  
      * The constructor.
 64  
      *
 65  
      * @param transition the transition represented by this notation
 66  
      */
 67  
     public TransitionNotationUml(Object transition) {
 68  0
         super(transition);
 69  0
     }
 70  
 
 71  
     /*
 72  
      * @see org.argouml.uml.notation.NotationProvider#parse(java.lang.Object, java.lang.String)
 73  
      */
 74  
     public void parse(Object modelElement, String text) {
 75  
         try {
 76  0
             parseTransition(modelElement, text);
 77  0
         } catch (ParseException pe) {
 78  0
             String msg = "statusmsg.bar.error.parsing.transition";
 79  0
             Object[] args = {
 80  
                 pe.getLocalizedMessage(),
 81  
                 Integer.valueOf(pe.getErrorOffset()),
 82  
             };
 83  0
             ArgoEventPump.fireEvent(new ArgoHelpEvent(
 84  
                     ArgoEventTypes.HELP_CHANGED, this,
 85  
                     Translator.messageFormat(msg, args)));
 86  0
         }
 87  0
     }
 88  
 
 89  
     /**
 90  
      * Parse a transition description line of the form:<pre>
 91  
      *    "event-signature [guard-condition] / action-expression".
 92  
      * </pre>
 93  
      *
 94  
      * A ";" is not interpreted as having any special meaning. <p>
 95  
      *
 96  
      * The "event-signature" may be one of the 4
 97  
      * formats:<ul>
 98  
      * <li> ChangeEvent: "when(condition)"
 99  
      * <li> TimeEvent: "after(duration)"
 100  
      * <li> CallEvent: "a(parameter-list)".
 101  
      * <li> SignalEvent: any string without ().
 102  
      * </ul>
 103  
      *
 104  
      * Remark: The UML standard does not make a distinction between
 105  
      * the syntax of a CallEvent and SignalEvent:
 106  
      * both may have parameters between ().
 107  
      * For simplicity and user-friendliness, we chose for this distinction.
 108  
      * If a user wants parameters for a SignalEvent,
 109  
      * then he may add them in the properties panels, but not on the diagram.
 110  
      * <p>
 111  
      *
 112  
      * An alternative solution would be to create a CallEvent by default,
 113  
      * and when editing an existing event, do not change the type.<p>
 114  
      *
 115  
      * TODO: This function fails when the event-signature contains a "["
 116  
      * or a "/". See issue 5983 for other cases that were 
 117  
      * a problem in the past.
 118  
      *
 119  
      * @param trans the transition object to which this string applies
 120  
      * @param s     the string to be parsed
 121  
      * @return      the transition object
 122  
      * @throws ParseException when no matching [] are found
 123  
      */
 124  
     protected Object parseTransition(Object trans, String s)
 125  
         throws ParseException {
 126  0
         s = s.trim();
 127  
 
 128  0
         int a = s.indexOf("[");
 129  0
         int b = s.indexOf("]");
 130  0
         int c = s.indexOf("/");
 131  0
         if (((a < 0) && (b >= 0)) || ((b < 0) && (a >= 0)) || (b < a)) {
 132  0
             String msg = "parsing.error.transition.no-matching-square-brackets";
 133  0
             throw new ParseException(Translator.localize(msg), 0);
 134  
         }
 135  0
         if ((c >= 0) && (c < b) && (c > a) && (a > 0)) {
 136  0
             String msg = "parsing.error.transition.found-bracket-instead-slash";
 137  0
             throw new ParseException(Translator.localize(msg), 0);
 138  
         }
 139  
 
 140  0
         String[] s1 = s.trim().split("/", 2);
 141  0
         String eg = s1[0].trim();
 142  0
         String[] s2 = eg.split("\\[", 2);
 143  
         
 144  0
         if (s2[0].trim().length() > 0) {
 145  0
             parseTrigger(trans, s2[0].trim());
 146  
         }
 147  0
         if (s2.length > 1)  {
 148  0
             if (s2[1].trim().endsWith("]")) {
 149  0
                 String g = s2[1].trim();
 150  0
                 g = g.substring(0, g.length() - 1).trim();
 151  0
                 if (g.length() > 0) {
 152  0
                     parseGuard(trans, g);
 153  
                 }
 154  
             }
 155  
         }
 156  0
         if (s1.length > 1)  {
 157  0
             if (s1[1].trim().length() > 0) {
 158  0
                 parseEffect(trans, s1[1].trim()); 
 159  
             }
 160  
         }
 161  0
         return trans;
 162  
     }
 163  
 
 164  
     /**
 165  
      * Parse the Event that is the trigger of the given transition.
 166  
      *
 167  
      * @param trans the transition which is triggered by the given event
 168  
      * @param trigger the given trigger
 169  
      * @throws ParseException
 170  
      */
 171  
     private void parseTrigger(Object trans, String trigger)
 172  
         throws ParseException {
 173  
         // let's look for a TimeEvent, ChangeEvent, CallEvent or SignalEvent
 174  0
         String s = "";
 175  0
         boolean timeEvent = false;
 176  0
         boolean changeEvent = false;
 177  0
         boolean callEvent = false;
 178  0
         boolean signalEvent = false;
 179  0
         trigger = trigger.trim();
 180  
 
 181  0
         StringTokenizer tokenizer = new StringTokenizer(trigger, "()");
 182  0
         String name = tokenizer.nextToken().trim();
 183  0
         if (name.equalsIgnoreCase("after")) {
 184  0
             timeEvent = true;
 185  0
         } else if (name.equalsIgnoreCase("when")) {
 186  0
             changeEvent = true;
 187  
         } else {
 188  
             // the part after the || is for when there's nothing between the ()
 189  0
             if (tokenizer.hasMoreTokens()
 190  
                     || (trigger.indexOf("(") > 0)
 191  
                     || (trigger.indexOf(")") > 1)) {
 192  0
                 callEvent = true;
 193  0
                 if (!trigger.endsWith(")") || !(trigger.indexOf("(") > 0)) {
 194  0
                     String msg =
 195  
                             "parsing.error.transition.no-matching-brackets";
 196  0
                     throw new ParseException(
 197  
                             Translator.localize(msg), 0);
 198  
                 }
 199  
             } else {
 200  0
                 signalEvent = true;
 201  
             }
 202  
         }
 203  0
         if (timeEvent || changeEvent || callEvent) {
 204  0
             if (tokenizer.hasMoreTokens()) {
 205  0
                 s = tokenizer.nextToken().trim();
 206  
             } // else the empty s will do
 207  
         }
 208  
 
 209  
         /*
 210  
          * We can distinguish between 4 cases:
 211  
          * 1. A trigger is given. None exists yet.
 212  
          * 2. The trigger was present, and it is the same type,
 213  
          * or a different type, and its text is changed, or the same.
 214  
          * 3. A trigger is not given. None exists yet.
 215  
          * 4. The name of the trigger was present, but is removed.
 216  
          * The reaction in these cases should be:
 217  
          * 1. Find the referred trigger (issue 5988) or create a new one, and hook it to the transition.
 218  
          * 2. Rename the trigger.
 219  
          * 3. Nop.
 220  
          * 4. Unhook and erase the existing trigger.
 221  
          */
 222  0
         Object evt = Model.getFacade().getTrigger(trans);
 223  
         /* It is safe to give a null to the next function,
 224  
          * since a statemachine is always composed by a model anyhow. */
 225  0
         Object ns =
 226  
             Model.getStateMachinesHelper()
 227  
                 .findNamespaceForEvent(trans, null);
 228  0
         StateMachinesFactory sMFactory =
 229  
                 Model.getStateMachinesFactory();
 230  0
         boolean weHaveAnEvent = false;
 231  0
         if (trigger.length() > 0) {
 232  
             // case 1 and 2
 233  0
             if (evt == null) {
 234  
                 // case 1
 235  0
                 if (timeEvent) { // after(...)
 236  0
                     evt = findOrBuildTimeEvent(s, ns);
 237  
                     /* Do not set the name. */
 238  
                 }
 239  0
                 if (changeEvent) { // when(...)
 240  0
                     evt = findOrBuildChangeEvent(s, ns);
 241  
                     /* Do not set the name. */
 242  
                 }
 243  0
                 if (callEvent) { // operation(paramlist)
 244  0
                     String triggerName =
 245  
                         trigger.indexOf("(") > 0
 246  
                         ? trigger.substring(0, trigger.indexOf("(")).trim()
 247  
                         : trigger;
 248  
                     /* This case is a bit different, because of the parameters. 
 249  
                      * If the event already exists, the parameters are ignored. */
 250  0
                     evt = findCallEvent(triggerName, ns);
 251  0
                     if (evt == null) {
 252  0
                         evt = sMFactory.buildCallEvent(trans, triggerName, ns);
 253  
                         // and parse the parameter list
 254  0
                         NotationUtilityUml.parseParamList(evt, s, 0);
 255  
                     }
 256  
                 }
 257  0
                 if (signalEvent) { // signalname
 258  0
                     evt = findOrBuildSignalEvent(trigger, ns);
 259  
                 }
 260  0
                 weHaveAnEvent = true;
 261  
             } else {
 262  
                 // case 2
 263  0
                 if (timeEvent) {
 264  0
                     if (Model.getFacade().isATimeEvent(evt)) {
 265  
                         /* Just change the time expression */
 266  0
                         Object timeExpr = Model.getFacade().getWhen(evt);
 267  0
                         if (timeExpr == null) {
 268  
                             // we have an event without expression
 269  0
                             timeExpr = Model.getDataTypesFactory().createTimeExpression("", s);
 270  0
                             Model.getStateMachinesHelper().setWhen(evt, timeExpr);
 271  
                         } else {
 272  0
                             Model.getDataTypesHelper().setBody(timeExpr, s);
 273  
                         }
 274  0
                     } else {
 275  
                         /* It's a time-event now,
 276  
                          * but was of another type before! */
 277  0
                         delete(evt); /* TODO: What if used elsewhere? */
 278  0
                         evt = sMFactory.buildTimeEvent(s, ns);
 279  0
                         weHaveAnEvent = true;
 280  
                     }
 281  
                 }
 282  0
                 if (changeEvent) {
 283  0
                     if (Model.getFacade().isAChangeEvent(evt)) {
 284  
                         /* Just change the ChangeExpression */
 285  0
                         Object changeExpr =
 286  
                             Model.getFacade().getChangeExpression(evt);
 287  0
                         if (changeExpr == null) {
 288  
                             /* Create a new expression: */
 289  0
                             changeExpr = Model.getDataTypesFactory()
 290  
                                 .createBooleanExpression("", s);
 291  0
                             Model.getStateMachinesHelper().setExpression(evt,
 292  
                                     changeExpr);
 293  
                         } else {
 294  0
                             Model.getDataTypesHelper().setBody(changeExpr, s);
 295  
                         }
 296  0
                     } else {
 297  
                         /* The parsed text describes a change-event,
 298  
                          * but the model contains another type! */
 299  0
                         delete(evt); /* TODO: What if used elsewhere? */
 300  0
                         evt = sMFactory.buildChangeEvent(s, ns);
 301  0
                         weHaveAnEvent = true;
 302  
                     }
 303  
                 }
 304  0
                 if (callEvent) {
 305  0
                     if (Model.getFacade().isACallEvent(evt)) {
 306  
                         /* Just change the Name and linked operation */
 307  0
                         String triggerName =
 308  
                             trigger.indexOf("(") > 0
 309  
                             ? trigger.substring(0, trigger.indexOf("(")).trim()
 310  
                                     : trigger;
 311  0
                         if (!Model.getFacade().getName(evt)
 312  
                                 .equals(triggerName)) {
 313  0
                             Model.getCoreHelper().setName(evt, triggerName);
 314  
                         }
 315  
                             /* TODO: Change the linked operation. */
 316  0
                     } else {
 317  0
                         delete(evt); /* TODO: What if used elsewhere? */
 318  0
                         evt = sMFactory.buildCallEvent(trans, trigger, ns);
 319  
                         // and parse the parameter list
 320  0
                         NotationUtilityUml.parseParamList(evt, s, 0);
 321  0
                         weHaveAnEvent = true;
 322  
                     }
 323  
                 }
 324  0
                 if (signalEvent) {
 325  0
                     if (Model.getFacade().isASignalEvent(evt)) {
 326  
                         /* Just change the Name and linked signal */
 327  0
                         if (!Model.getFacade().getName(evt).equals(trigger)) {
 328  0
                             Model.getCoreHelper().setName(evt, trigger);
 329  
                         }
 330  
                         /* TODO: link to the Signal. */
 331  
                     } else {
 332  0
                         delete(evt); /* TODO: What if used elsewhere? */
 333  0
                         evt = sMFactory.buildSignalEvent(trigger, ns);
 334  0
                         weHaveAnEvent = true;
 335  
                     }
 336  
                 }
 337  
             }
 338  0
             if (weHaveAnEvent && (evt != null)) {
 339  0
                 Model.getStateMachinesHelper().setEventAsTrigger(trans, evt);
 340  
             }
 341  
         } else {
 342  
             // case 3 and 4
 343  0
             if (evt == null) {
 344  
                 /* case 3 */
 345  
             } else {
 346  
                 // case 4
 347  0
                 delete(evt); // erase it
 348  
             }
 349  
         }
 350  0
     }
 351  
     
 352  
     protected Object findOrBuildSignalEvent(String trigger, Object ns) {
 353  0
         StateMachinesFactory sMFactory = Model.getStateMachinesFactory();
 354  0
         if ((trigger == null) || ("".equals(trigger.trim()))) {
 355  0
             return sMFactory.buildSignalEvent(trigger, ns);
 356  
         }
 357  0
         Object result = null;
 358  0
         Object type = Model.getMetaTypes().getSignalEvent();
 359  0
         Collection events = Model.getModelManagementHelper()
 360  
             .getAllModelElementsOfKind(ns, type);
 361  0
         for (Object event : events) {
 362  0
             if (trigger.equals(Model.getFacade().getName(event))) {
 363  0
                 result = event;
 364  0
                 break;
 365  
             }
 366  
         }
 367  0
         if (result == null) {
 368  0
             result = sMFactory.buildSignalEvent(trigger, ns);
 369  
         }
 370  0
         return result;
 371  
     }
 372  
     
 373  
     protected Object findOrBuildTimeEvent(String timeexpr, Object ns) {
 374  0
         StateMachinesFactory sMFactory = Model.getStateMachinesFactory();
 375  0
         if ((timeexpr == null) || ("".equals(timeexpr.trim()))) {
 376  0
             return sMFactory.buildTimeEvent(timeexpr, ns);
 377  
         }
 378  0
         Object result = null;
 379  0
         Object type = Model.getMetaTypes().getTimeEvent();
 380  0
         Collection events = Model.getModelManagementHelper()
 381  
             .getAllModelElementsOfKind(ns, type);
 382  0
         for (Object event : events) {
 383  0
             Object expression = Model.getFacade().getExpression(event);
 384  0
             if (expression != null) {
 385  0
                 if (timeexpr.equals(Model.getFacade().getBody(expression))) {
 386  0
                     result = event;
 387  0
                     break;
 388  
                 }
 389  
             }
 390  0
         }
 391  0
         if (result == null) {
 392  0
             result = sMFactory.buildTimeEvent(timeexpr, ns);
 393  
         }
 394  0
         return result;
 395  
     }
 396  
     
 397  
     protected Object findOrBuildChangeEvent(String changeexpr, Object ns) {
 398  0
         StateMachinesFactory sMFactory = Model.getStateMachinesFactory();
 399  0
         if ((changeexpr == null) || ("".equals(changeexpr.trim()))) {
 400  0
             return sMFactory.buildChangeEvent(changeexpr, ns);
 401  
         }
 402  0
         Object result = null;
 403  0
         Object type = Model.getMetaTypes().getChangeEvent();
 404  0
         Collection events = Model.getModelManagementHelper()
 405  
             .getAllModelElementsOfKind(ns, type);
 406  0
         for (Object event : events) {
 407  0
             Object expression = Model.getFacade().getExpression(event);
 408  0
             if (expression != null) {
 409  0
                 if (changeexpr.equals(Model.getFacade().getBody(expression))) {
 410  0
                     result = event;
 411  0
                     break;
 412  
                 }
 413  
             }
 414  0
         }
 415  0
         if (result == null) {
 416  0
             result = sMFactory.buildChangeEvent(changeexpr, ns);
 417  
         }
 418  0
         return result;
 419  
     }
 420  
     
 421  
     protected Object findCallEvent(String callexpr, Object ns) {
 422  0
         if ((callexpr == null) || ("".equals(callexpr.trim()))) {
 423  0
             return null;
 424  
         }
 425  0
         Object result = null;
 426  0
         Object type = Model.getMetaTypes().getCallEvent();
 427  0
         Collection events = Model.getModelManagementHelper()
 428  
             .getAllModelElementsOfKind(ns, type);
 429  0
         for (Object event : events) {
 430  0
             if (callexpr.equals(Model.getFacade().getName(event))) {
 431  
                 /* Do not check if the parameters match. */
 432  0
                 result = event;
 433  0
                 break;
 434  
             }
 435  
         }
 436  0
         return result;
 437  
     }
 438  
 
 439  
     /**
 440  
      * Handle the Guard of a Transition.<p>
 441  
      *
 442  
      * We can distinct between 4 cases:<ol>
 443  
      * <li>A guard is given. None exists yet.
 444  
      * <li>The expression of the guard was present, but is altered.
 445  
      * <li>A guard is not given. None exists yet.
 446  
      * <li>The expression of the guard was present, but is removed.
 447  
      * </ol>
 448  
      *
 449  
      * The reaction in these cases should be:<ol>
 450  
      * <li>Create a new guard, set its name, language & expression,
 451  
      *     and hook it to the transition.
 452  
      * <li>Change the guard's expression. Leave the name & language
 453  
      *     untouched. See also issue 2742.
 454  
      * <li>Nop.
 455  
      * <li>Unhook and erase the existing guard.
 456  
      * </ol>
 457  
      *
 458  
      * @param trans the UML element transition
 459  
      * @param guard the string that represents the guard expression
 460  
      */
 461  
     private void parseGuard(Object trans, String guard) {
 462  0
         Object g = Model.getFacade().getGuard(trans);
 463  0
         if (guard.length() > 0) {
 464  0
             if (g == null) {
 465  
                 // case 1
 466  
                 /*TODO: In the next line, I should use buildGuard(),
 467  
                  * but it doesn't show the guard on the diagram...
 468  
                  * Why? (MVW)
 469  
                  */
 470  0
                 g = Model.getStateMachinesFactory().createGuard();
 471  0
                 if (g != null) {
 472  0
                     Model.getStateMachinesHelper().setExpression(g,
 473  
                             Model.getDataTypesFactory()
 474  
                                 .createBooleanExpression("", guard));
 475  0
                     Model.getCoreHelper().setName(g, "anon");
 476  0
                     Model.getCommonBehaviorHelper().setTransition(g, trans);
 477  
 
 478  
                     // NSUML does this (?)
 479  
                     // Model.getFacade().setGuard(trans, g);
 480  
                 }
 481  
             } else {
 482  
                 // case 2
 483  0
                 Object expr = Model.getFacade().getExpression(g);
 484  0
                 String language = "";
 485  
 
 486  
                 /* TODO: This does not work! (MVW)
 487  
                  Model.getFacade().setBody(expr,guard);
 488  
                  Model.getFacade().setExpression(g,expr); */
 489  
 
 490  
                 //hence a less elegant workaround that works:
 491  0
                 if (expr != null) {
 492  0
                     language = Model.getDataTypesHelper().getLanguage(expr);
 493  
                 }
 494  0
                 Model.getStateMachinesHelper().setExpression(g,
 495  
                         Model.getDataTypesFactory()
 496  
                                 .createBooleanExpression(language, guard));
 497  
                 /* TODO: In this case, the properties panel
 498  
                  is not updated with the changed expression! */
 499  0
             }
 500  
         } else {
 501  0
             if (g == null) {
 502  
                 /* case 3 */
 503  
             } else {
 504  
                 // case 4
 505  0
                 delete(g); // erase it
 506  
             }
 507  
         }
 508  0
     }
 509  
 
 510  
     /**
 511  
      * Handle the Effect (Action) of a Transition.<p>
 512  
      *
 513  
      * We can distinct between 4 cases:<ul>
 514  
      * <li>1. An effect is given. None exists yet.
 515  
      * <li>2. The expression of the effect was present, but is altered.
 516  
      * <li>3. An effect is not given. None exists yet.
 517  
      * <li>4. The expression of the effect was present, but is removed.
 518  
      * </ul>
 519  
      *
 520  
      * The reaction in these cases should be:<ul>
 521  
      * <li>1. Create a new CallAction, set its name, language &
 522  
      * expression, and hook it to the transition.
 523  
      * <li>2. Change the effect's expression. Leave the actiontype, name
 524  
      * & language untouched.
 525  
      * <li>3. Nop.
 526  
      * <li>4. Unhook and erase the existing effect.
 527  
      * </ul>
 528  
      *
 529  
      * @param actions the string to be parsed
 530  
      * @param trans the transition that causes the effect (actions)
 531  
      */
 532  
     private void parseEffect(Object trans, String actions) {
 533  0
         Object effect = Model.getFacade().getEffect(trans);
 534  0
         if (actions.length() > 0) {
 535  0
             if (effect == null) { // case 1
 536  0
                 effect =
 537  
                     Model.getCommonBehaviorFactory()
 538  
                         .createCallAction();
 539  
                 /* And hook it to the transition immediately,
 540  
                  * so that an exception can not cause it to remain dangling: */
 541  0
                 Model.getStateMachinesHelper().setEffect(trans, effect);
 542  0
                 Model.getCommonBehaviorHelper().setScript(effect,
 543  
                         Model.getDataTypesFactory()
 544  
                                 .createActionExpression(""/*language*/,
 545  
                                                         actions));
 546  0
                 Model.getCoreHelper().setName(effect, "anon");
 547  
             } else { // case 2
 548  0
                 Object script = Model.getFacade().getScript(effect);
 549  0
                 String language = (script == null) ? null
 550  
                         : Model.getDataTypesHelper().getLanguage(script);
 551  0
                 Model.getCommonBehaviorHelper().setScript(effect,
 552  
                         Model.getDataTypesFactory()
 553  
                                 .createActionExpression(language, actions));
 554  0
             }
 555  
         } else { // case 3 & 4
 556  0
             if (effect == null) {
 557  
                 // case 3
 558  
             } else {
 559  
                 // case 4
 560  0
                 delete(effect); // erase it
 561  
             }
 562  
         }
 563  0
     }
 564  
 
 565  
     /**
 566  
      * This deletes modelelements, and swallows null without barking.
 567  
      *
 568  
      * @author Michiel van der Wulp
 569  
      * @param obj
 570  
      *            the modelelement to be deleted
 571  
      */
 572  
     private void delete(Object obj) {
 573  0
         if (obj != null) {
 574  0
             Model.getUmlFactory().delete(obj);
 575  
         }
 576  0
     }
 577  
 
 578  
     /*
 579  
      * @see org.argouml.uml.notation.NotationProvider#getParsingHelp()
 580  
      */
 581  
     public String getParsingHelp() {
 582  0
         return "parsing.help.fig-transition";
 583  
     }
 584  
 
 585  
     @Override
 586  
     public String toString(Object modelElement, NotationSettings settings) {
 587  0
         return toString(modelElement);
 588  
     }
 589  
 
 590  
     private String toString(Object modelElement) {
 591  0
         Object trigger = Model.getFacade().getTrigger(modelElement);
 592  0
             Object guard = Model.getFacade().getGuard(modelElement);
 593  0
         Object effect = Model.getFacade().getEffect(modelElement);
 594  0
         String t = generateEvent(trigger);
 595  0
         String g = generateGuard(guard);
 596  0
         String e = NotationUtilityUml.generateActionSequence(effect);
 597  0
         if (g.length() > 0) {
 598  0
             t += " [" + g + "]";
 599  
         }
 600  0
         if (e.length() > 0) {
 601  0
             t += " / " + e;
 602  
         }
 603  0
         return t;
 604  
     }
 605  
 
 606  
     /**
 607  
      * Generates the text for a (trigger) event.
 608  
      *
 609  
      * @param m Object of any MEvent kind
 610  
      * @return the string representing the event
 611  
      */
 612  
     private String generateEvent(Object m) {
 613  0
         if (m == null) {
 614  0
             return "";
 615  
         }
 616  0
         StringBuffer event = new StringBuffer();
 617  0
         if (Model.getFacade().isAChangeEvent(m)) {
 618  0
             event.append("when(");
 619  0
             event.append(
 620  
                     generateExpression(Model.getFacade().getExpression(m)));
 621  0
             event.append(")");
 622  0
         } else if (Model.getFacade().isATimeEvent(m)) {
 623  0
             event.append("after(");
 624  0
             event.append(
 625  
                     generateExpression(Model.getFacade().getExpression(m)));
 626  0
             event.append(")");
 627  0
         } else if (Model.getFacade().isASignalEvent(m)) {
 628  0
             event.append(Model.getFacade().getName(m));
 629  0
         } else if (Model.getFacade().isACallEvent(m)) {
 630  0
             event.append(Model.getFacade().getName(m));
 631  0
             event.append(generateParameterList(m));
 632  
         }
 633  0
         return event.toString();
 634  
     }
 635  
 
 636  
     /**
 637  
      * Generates a string representing the given Guard. <p>
 638  
      *
 639  
      * If there is an expression, then its body text is returned.
 640  
      * Else, a 0 length string is returned. <p>
 641  
      *
 642  
      * Apparently, the AndroMDA people are convinced that the
 643  
      * name of the guard should be shown on the diagram, while this
 644  
      * is not correct according the UML standard.
 645  
      * ArgoUML does not support this feature because of its down-side:
 646  
      * The name would end up in the expression
 647  
      * if you edit the transition text on the diagram,
 648  
      * while the name remains containing the old value.
 649  
      *
 650  
      * @param m the UML Guard object
 651  
      * @return a string
 652  
      */
 653  
     private String generateGuard(Object m) {
 654  0
         if (m != null) {
 655  0
             if (Model.getFacade().getExpression(m) != null) {
 656  0
                 return generateExpression(Model.getFacade().getExpression(m));
 657  
             }
 658  
         }
 659  0
         return "";
 660  
     }
 661  
 
 662  
     /**
 663  
      * Generates a list of parameters. The parameters belong to the
 664  
      * given object.  The returned string will have the following
 665  
      * syntax:<p>
 666  
      *
 667  
      * (param1, param2, param3, ..., paramN)<p>
 668  
      *
 669  
      * If there are no parameters, then "()" is returned.
 670  
      *
 671  
      * @param parameterListOwner the 'owner' of the parameters
 672  
      * @return the generated parameter list
 673  
      */
 674  
     private String generateParameterList(Object parameterListOwner) {
 675  0
         Iterator it =
 676  
             Model.getFacade().getParameters(parameterListOwner).iterator();
 677  0
         StringBuffer list = new StringBuffer();
 678  0
         list.append("(");
 679  0
         if (it.hasNext()) {
 680  0
             while (it.hasNext()) {
 681  0
                 Object param = it.next();
 682  0
                 list.append(generateParameter(param));
 683  0
                 if (it.hasNext()) {
 684  0
                     list.append(", ");
 685  
                 }
 686  0
             }
 687  
         }
 688  0
         list.append(")");
 689  0
         return list.toString();
 690  
     }
 691  
 
 692  
     private String generateExpression(Object expr) {
 693  0
         if (Model.getFacade().isAExpression(expr)) {
 694  0
             Object body = Model.getFacade().getBody(expr);
 695  0
             if (body != null) {
 696  0
                 return (String) body;
 697  
             }
 698  
         }
 699  0
         return "";
 700  
     }
 701  
 
 702  
     /**
 703  
      * Generates the representation of a parameter on the display
 704  
      * (diagram). The string to be returned will have the following
 705  
      * syntax:<p>
 706  
      *
 707  
      * kind name : type-expression = default-value
 708  
      *
 709  
      * @param parameter the parameter
 710  
      * @return the generated text
 711  
      */
 712  
     public String generateParameter(Object parameter) {
 713  0
         StringBuffer s = new StringBuffer();
 714  0
         s.append(generateKind(Model.getFacade().getKind(parameter)));
 715  0
         if (s.length() > 0) {
 716  0
             s.append(" ");
 717  
         }
 718  0
         s.append(Model.getFacade().getName(parameter));
 719  0
         String classRef =
 720  
             generateClassifierRef(Model.getFacade().getType(parameter));
 721  0
         if (classRef.length() > 0) {
 722  0
             s.append(" : ");
 723  0
             s.append(classRef);
 724  
         }
 725  0
         String defaultValue =
 726  
             generateExpression(Model.getFacade().getDefaultValue(parameter));
 727  0
         if (defaultValue.length() > 0) {
 728  0
             s.append(" = ");
 729  0
             s.append(defaultValue);
 730  
         }
 731  0
         return s.toString();
 732  
     }
 733  
 
 734  
     private String generateKind(Object /*Parameter etc.*/ kind) {
 735  0
         StringBuffer s = new StringBuffer();
 736  0
         if (kind == null /* "in" is the default */
 737  
                 || kind == Model.getDirectionKind().getInParameter()) {
 738  0
             s.append(/*"in"*/ ""); /* See issue 3421. */
 739  0
         } else if (kind == Model.getDirectionKind().getInOutParameter()) {
 740  0
             s.append("inout");
 741  0
         } else if (kind == Model.getDirectionKind().getReturnParameter()) {
 742  
             // return nothing
 743  0
         } else if (kind == Model.getDirectionKind().getOutParameter()) {
 744  0
             s.append("out");
 745  
         }
 746  0
         return s.toString();
 747  
     }
 748  
 
 749  
     /**
 750  
      * Generate the type of a parameter, i.e. a reference to a classifier.
 751  
      *
 752  
      * @param cls the classifier
 753  
      * @return the generated text
 754  
      */
 755  
     private String generateClassifierRef(Object cls) {
 756  0
         if (cls == null) {
 757  0
             return "";
 758  
         }
 759  0
         return Model.getFacade().getName(cls);
 760  
     }
 761  
 
 762  
 }