Coverage Report - org.argouml.notation.providers.uml.AbstractMessageNotationUml
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractMessageNotationUml
0%
0/726
0%
0/566
9.2
AbstractMessageNotationUml$MsgPtr
0%
0/1
N/A
9.2
 
 1  
 /* $Id: AbstractMessageNotationUml.java 17828 2010-01-12 18:55:12Z linus $
 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  
  *    mvw
 11  
  *****************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 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.ArrayList;
 43  
 import java.util.Collection;
 44  
 import java.util.Iterator;
 45  
 import java.util.List;
 46  
 import java.util.NoSuchElementException;
 47  
 
 48  
 import org.apache.log4j.Logger;
 49  
 import org.argouml.application.events.ArgoEventPump;
 50  
 import org.argouml.application.events.ArgoEventTypes;
 51  
 import org.argouml.application.events.ArgoHelpEvent;
 52  
 import org.argouml.i18n.Translator;
 53  
 import org.argouml.kernel.ProjectManager;
 54  
 import org.argouml.model.Model;
 55  
 import org.argouml.notation.providers.MessageNotation;
 56  
 import org.argouml.util.CustomSeparator;
 57  
 import org.argouml.util.MyTokenizer;
 58  
 
 59  
 /**
 60  
  * This abstract class provides the common functionality for
 61  
  * the UML notation of messages.<br/>
 62  
  * It is extended by {@link MessageNotationUml}, with the 
 63  
  * notation of messages as seen in collaboration diagrams, 
 64  
  * and {@link SDMessageNotationUml}, with the notation of 
 65  
  * messages as seen in sequence diagrams.<p>
 66  
  * 
 67  
  * The Message notation syntax is a line of the following form,
 68  
  * which we can generate and parse: <p>
 69  
  *
 70  
  * <pre>
 71  
  * intno := integer|name
 72  
  * seq := intno ['.' intno]*
 73  
  * recurrence := '*'['//'] | '*'['//']'[' <i>iteration </i>']' | '['
 74  
  * <i>condition </i>']'
 75  
  * seqelem := {[intno] ['['recurrence']']}
 76  
  * seq_expr := seqelem ['.' seqelem]*
 77  
  * ret_list := lvalue [',' lvalue]*
 78  
  * arg_list := rvalue [',' rvalue]*
 79  
  * predecessor := seq [',' seq]* '/'
 80  
  * message := [predecessor] seq_expr ':' [ret_list :=] name ([arg_list])
 81  
  * </pre>
 82  
  *
 83  
  * Which is rather complex, so a few examples:<ul>
 84  
  * <li> 2: display(x, y)
 85  
  * <li> 1.3.1: p := find(specs)
 86  
  * <li> [x &lt; 0] 4: invert(color)
 87  
  * <li> A3, B4/ C3.1*: update()
 88  
  * </ul>
 89  
  *
 90  
  * This syntax is compatible with the UML 1.4.2 specification.<p>
 91  
  *
 92  
  * Actually, only a subset of this syntax is currently supported, and some
 93  
  * is not even planned to be supported. The exceptions are intno, which only
 94  
  * allows a number possibly followed by a sequence of letters in the range
 95  
  * 'a' - 'z', seqelem, which does not allow a recurrence, and message, which
 96  
  * does allow one recurrence near seq_expr. <p>
 97  
  * 
 98  
  * (formerly, the supported syntax was: name: action ) <p>
 99  
  *
 100  
  * Generating a string from the model has some extra functionality:
 101  
  * If obtaining the Script of the Action returns an empty string, 
 102  
  * then an alternative representation is given:
 103  
  * If the action is a CallAction, use the name of its Operation, 
 104  
  * and if it is a SendAction, the name of its Signal.
 105  
  * If also this returns no string, then we display the name of the Message. <p>
 106  
  * 
 107  
  *  Rationale:
 108  
  *  This allows ArgoUML to show something on the diagram with older projects, 
 109  
  *  which only had the name of the Message filled in by the user.
 110  
  *  This also may improve the diagrams for imported XMI.<p>
 111  
  *
 112  
  * Parsing a text that is generated by one of the backup scenarios, 
 113  
  * causes it to be written back in the script of the Action.
 114  
  * Hence, editing the text on the diagram only once 
 115  
  * causes the Action Script to be used from then on. <p>
 116  
  * 
 117  
  * Supported operations for the parser: <p>
 118  
  * <ul>
 119  
  * <li>Locating an Operation by name and the number of arguments - 
 120  
  * the operation is hooked to the CallAction of the Message.
 121  
  * <li>Create an Operation with given name (no arguments).
 122  
  * <li>Change the order of messages (predecessor/successor).
 123  
  * <li>Reverting the direction of a message.
 124  
  * <li>etc.
 125  
  * </ul>
 126  
  * 
 127  
  * @see MessageNotationUml
 128  
  * @see SDMessageNotationUml
 129  
  * @since 0.28.alpha1
 130  
  * @author penyaskito
 131  
  */
 132  0
 public abstract class AbstractMessageNotationUml extends MessageNotation {
 133  
 
 134  0
     private static final Logger LOG =
 135  
         Logger.getLogger(AbstractMessageNotationUml.class);
 136  
     
 137  
     /**
 138  
      * The list of CustomSeparators to use when tokenizing parameters.
 139  
      */
 140  
     private final List<CustomSeparator> parameterCustomSep;
 141  
 
 142  
     /**
 143  
      * An object containing an UML Message object.
 144  
      */
 145  0
     protected static class MsgPtr {
 146  
         /**
 147  
          * The message pointed to.
 148  
          */
 149  
         Object message;
 150  
     }
 151  
 
 152  
     /**
 153  
      * @param umlMessage the UML Message object
 154  
      */
 155  
     public AbstractMessageNotationUml(Object umlMessage) {
 156  0
         super(umlMessage);
 157  0
         parameterCustomSep = initParameterSeparators();
 158  0
     }
 159  
 
 160  
     protected String toString(final Object umlMessage, 
 161  
             boolean showSequenceNumbers) {
 162  
         Iterator it;
 163  
         Collection umlPredecessors;
 164  
         Object umlAction;
 165  
         Object umlActivator; // this is a Message UML object
 166  
         MsgPtr ptr;
 167  
         int lpn;
 168  
         
 169  
         /* Supported format: 
 170  
          *     predecessors number ":" action
 171  
          * The 3 parts of the string to generate: */
 172  0
         StringBuilder predecessors = new StringBuilder(); // includes the "/"
 173  
         String number; // the "seq_expr" from the header javadoc
 174  
         // the ":" is not included in "number" - it is always present
 175  0
         String action = "";
 176  
 
 177  0
         if (umlMessage == null) {
 178  0
             return "";
 179  
         }
 180  
 
 181  0
         ptr = new MsgPtr();
 182  0
         lpn = recCountPredecessors(umlMessage, ptr) + 1;
 183  0
         umlActivator = Model.getFacade().getActivator(umlMessage);
 184  
 
 185  0
         umlPredecessors = Model.getFacade().getPredecessors(umlMessage);
 186  0
         it = (umlPredecessors != null) ? umlPredecessors.iterator() : null;
 187  0
         if (it != null && it.hasNext()) {
 188  0
             MsgPtr ptr2 = new MsgPtr();
 189  0
             int precnt = 0;
 190  
 
 191  0
             while (it.hasNext()) {
 192  0
                 Object msg = /*(MMessage)*/ it.next();
 193  0
                 int mpn = recCountPredecessors(msg, ptr2) + 1;
 194  
 
 195  0
                 if (mpn == lpn - 1
 196  
                     && umlActivator == Model.getFacade().getActivator(msg)
 197  
                     && Model.getFacade().getPredecessors(msg).size() < 2
 198  
                     && (ptr2.message == null
 199  
                         || countSuccessors(ptr2.message) < 2)) {
 200  0
                     continue;
 201  
                 }
 202  
 
 203  0
                 if (predecessors.length() > 0) {
 204  0
                     predecessors.append(", ");
 205  
                 }
 206  0
                 predecessors.append(
 207  
                         generateMessageNumber(msg, ptr2.message, mpn));
 208  0
                 precnt++;
 209  0
             }
 210  
 
 211  0
             if (precnt > 0) {
 212  0
                 predecessors.append(" / ");
 213  
             }
 214  
         }
 215  
 
 216  0
         number = generateMessageNumber(umlMessage, ptr.message, lpn);
 217  
 
 218  0
         umlAction = Model.getFacade().getAction(umlMessage);
 219  0
         if (umlAction != null) {
 220  0
             if (Model.getFacade().getRecurrence(umlAction) != null) {
 221  0
                 number = generateRecurrence(
 222  
                         Model.getFacade().getRecurrence(umlAction))
 223  
                     + " "
 224  
                     + number;
 225  
                 /* TODO: The recurrence goes in front of the action? 
 226  
                  * Does this not contradict the header JavaDoc? */
 227  
             }
 228  
         }
 229  0
         action = NotationUtilityUml.generateActionSequence(umlAction);
 230  0
         if ("".equals(action) || action.trim().startsWith("(")) {
 231  
             /* If the script of the Action is empty,
 232  
              * (or only specifies arguments and no method name) 
 233  
              * then we generate a string based on 
 234  
              * a different model element: */
 235  0
             action = getInitiatorOfAction(umlAction);
 236  0
             if ("".equals(action)) {
 237  
                 // This may return null:
 238  0
                 String n = Model.getFacade().getName(umlMessage);
 239  0
                 if (n != null) {
 240  0
                     action = n;
 241  
                 }
 242  0
             }
 243  
         }
 244  0
         else if (!action.endsWith(")")) {
 245  
             /* Dirty fix for issue 1758 (Needs to be amended
 246  
              * when we start supporting parameters):
 247  
              */
 248  0
             action = action + "()";
 249  
         }
 250  
 
 251  0
         if (!showSequenceNumbers) {
 252  0
             return action;
 253  
         }
 254  0
         return predecessors + number + " : " + action;
 255  
     }
 256  
 
 257  
     protected String getInitiatorOfAction(Object umlAction) {
 258  0
         String result = "";
 259  0
         if (Model.getFacade().isACallAction(umlAction)) {
 260  0
             Object umlOperation = Model.getFacade().getOperation(umlAction);
 261  0
             if (Model.getFacade().isAOperation(umlOperation)) {
 262  0
                 StringBuilder sb = new StringBuilder(
 263  
                         Model.getFacade().getName(umlOperation));
 264  0
                 if (sb.length() > 0) {
 265  0
                     sb.append("()");
 266  0
                     result = sb.toString();
 267  
                 }
 268  
             }
 269  0
         } else if (Model.getFacade().isASendAction(umlAction)) {
 270  0
             Object umlSignal = Model.getFacade().getSignal(umlAction);
 271  0
             if (Model.getFacade().isASignal(umlSignal)) {
 272  0
                 String n = Model.getFacade().getName(umlSignal);
 273  0
                 if (n != null) {
 274  0
                     result = n;
 275  
                 }
 276  
             }
 277  
         }
 278  0
         return result;
 279  
     }
 280  
     
 281  
     protected List<CustomSeparator> initParameterSeparators() {
 282  0
         List<CustomSeparator> separators = new ArrayList<CustomSeparator>();
 283  0
         separators.add(MyTokenizer.SINGLE_QUOTED_SEPARATOR);
 284  0
         separators.add(MyTokenizer.DOUBLE_QUOTED_SEPARATOR);
 285  0
         separators.add(MyTokenizer.PAREN_EXPR_STRING_SEPARATOR);        
 286  0
         return separators;
 287  
     }
 288  
 
 289  
     public void parse(final Object umlMessage, final String text) {
 290  
         try {
 291  0
             parseMessage(umlMessage, text);
 292  0
         } catch (ParseException pe) {
 293  0
             final String msg = "statusmsg.bar.error.parsing.message";
 294  0
             final Object[] args = {pe.getLocalizedMessage(),
 295  
                 Integer.valueOf(pe.getErrorOffset()), };
 296  0
             ArgoEventPump.fireEvent(new ArgoHelpEvent(
 297  
                     ArgoEventTypes.HELP_CHANGED, this,
 298  
                     Translator.messageFormat(msg, args)));
 299  0
         }
 300  0
     }
 301  
 
 302  
     public String getParsingHelp() {
 303  0
         return "parsing.help.fig-message";
 304  
     }
 305  
 
 306  
     /**
 307  
      * Generate the "intno" of the given Message. <p>
 308  
      * 
 309  
      * If the predecessor of the given message has only one successor, then
 310  
      * we return the string representation of the given integer. <p>
 311  
      * If the predecessor of the given message has more than one successor, then
 312  
      * this is a case of parallel execution of messages, e.g. 
 313  
      * Message 3.1a and Message 3.1b are concurrent within activation 3.1.
 314  
      * Hence In this case we use a syntax like: 1a, 1b, 1c. 
 315  
      *  
 316  
      * This means that the first successor 
 317  
      * in the ordered list of successors that has more than one entry 
 318  
      * will get the postfix a, the second b, etc.
 319  
      *  
 320  
      * TODO: Document exceptional behaviour.
 321  
      * 
 322  
      * @param umlMessage the UML message object to generate 
 323  
      * the sequence number for
 324  
      * @param umlPredecessor the immediate predecessor message (UML object)
 325  
      * that has the given message as successor
 326  
      * @param position the integer position of the given message 
 327  
      * within its sequence
 328  
      * @return the generated sequence expression string, 
 329  
      * or null if the given Message was null
 330  
      */
 331  
     protected String generateMessageNumber(Object umlMessage, 
 332  
             Object umlPredecessor,
 333  
             int position) {
 334  
         Iterator it;
 335  0
         String activatorIntNo = "";
 336  
         Object umlActivator;
 337  0
         int subpos = 0, submax = 1;
 338  
 
 339  0
         if (umlMessage == null) {
 340  0
             return null;
 341  
         }
 342  
 
 343  0
         umlActivator = Model.getFacade().getActivator(umlMessage);
 344  0
         if (umlActivator != null) {
 345  0
             activatorIntNo = generateMessageNumber(umlActivator);
 346  
             // activatorIntNo is now guaranteed not null
 347  
         }
 348  
 
 349  0
         if (umlPredecessor != null) {
 350  
             // get the ordered list of immediate successors:
 351  0
             Collection c = Model.getFacade().getSuccessors(umlPredecessor);
 352  0
             submax = c.size();
 353  0
             it = c.iterator();
 354  0
             while (it.hasNext() && it.next() != umlMessage) {
 355  0
                 subpos++;
 356  
             }
 357  
         }
 358  
 
 359  0
         StringBuilder result = new StringBuilder(activatorIntNo);
 360  0
         if (activatorIntNo.length() > 0) {
 361  0
             result.append(".");
 362  
         }
 363  0
         result.append(position);
 364  0
         if (submax > 1) {
 365  0
             result.append((char) ('a' + subpos));
 366  
         }
 367  0
         return result.toString();
 368  
     }
 369  
 
 370  
     /**
 371  
      * Finds the break between message number and (possibly) message order.
 372  
      *
 373  
      * @return The position of the end of the number.
 374  
      */
 375  
     private static int findMsgOrderBreak(String s) {
 376  
         int i, t;
 377  
     
 378  0
         t = s.length();
 379  0
         for (i = 0; i < t; i++) {
 380  0
             char c = s.charAt(i);
 381  0
             if (c < '0' || c > '9') {
 382  0
                 break;
 383  
             }
 384  
         }
 385  0
         return i;
 386  
     }
 387  
 
 388  
     /**
 389  
      * Generates the textual number of a given Message, called seq_expr. 
 390  
      * The seq_expr is a string of numbers separated by points 
 391  
      * which describes the message's order
 392  
      * and level in a collaboration.<p>
 393  
      *
 394  
      * If you plan to modify this seq_expr, make sure that
 395  
      * the parsing of the Message is adapted accordingly to the change.
 396  
      *
 397  
      * @param message A Message to generate the seq_expr for
 398  
      * @return A String with the seq_expr of the given message,
 399  
      * or null if the given message was null
 400  
      */
 401  
     private String generateMessageNumber(Object message) {
 402  0
         MsgPtr ptr = new MsgPtr();
 403  0
         int pos = recCountPredecessors(message, ptr) + 1;
 404  0
         return generateMessageNumber(message, ptr.message, pos);
 405  
     }
 406  
 
 407  
     /**
 408  
      * Count the number of successors of the given Message. <p>
 409  
      * 
 410  
      * Successors have the same Activator as the given message.
 411  
      * This Activator may be null.
 412  
      * 
 413  
      * @param message the UML Message object
 414  
      * @return the number of successors: 0..n
 415  
      */
 416  
     protected int countSuccessors(Object message) {
 417  0
         int count = 0;
 418  0
         final Object activator = Model.getFacade().getActivator(message);
 419  0
         final Collection successors = Model.getFacade().getSuccessors(message);
 420  0
         if (successors != null) {
 421  0
             for (Object suc : successors) {
 422  0
                 if (Model.getFacade().getActivator(suc) != activator) {
 423  0
                     continue;
 424  
                 }
 425  0
                 count++;
 426  
             }
 427  
         }
 428  0
         return count;
 429  
     }
 430  
 
 431  
     /**
 432  
      * Generates a textual description of an IterationExpression.
 433  
      *
 434  
      * @param expr the given UML expression object or null
 435  
      * @return the string
 436  
      */
 437  
     protected String generateRecurrence(Object expr) {
 438  0
         if (expr == null) {
 439  0
             return "";
 440  
         }
 441  
     
 442  0
         return Model.getFacade().getBody(expr).toString();
 443  
     }
 444  
 
 445  
     /**
 446  
      * Parse a Message textual description.<p>
 447  
      *
 448  
      * TODO: - This method is too complex, lets break it up. <p>
 449  
      *
 450  
      * @param umlMessage the UML Message object to apply any changes to
 451  
      * @param s   the String to parse
 452  
      * @throws ParseException
 453  
      *            when it detects an error in the attribute string. See also
 454  
      *            ParseError.getErrorOffset().
 455  
      */
 456  
     protected void parseMessage(Object umlMessage, String s)
 457  
         throws ParseException {
 458  0
         String fname = null;
 459  
      // the condition or iteration expression (recurrence):
 460  0
         StringBuilder guard = null;
 461  0
         String paramExpr = null;
 462  
         String token;
 463  0
         StringBuilder varname = null;
 464  0
         List<List> predecessors = new ArrayList<List>();
 465  0
         List<Integer> seqno = null;
 466  0
         List<Integer> currentseq = new ArrayList<Integer>();
 467  
 //        List<String> args = null;
 468  0
         boolean mustBePre = false;
 469  0
         boolean mustBeSeq = false;
 470  0
         boolean parallell = false;
 471  0
         boolean iterative = false;
 472  0
         boolean mayDeleteExpr = false;
 473  0
         boolean refindOperation = false;
 474  0
         boolean hasPredecessors = false;
 475  
 
 476  0
         currentseq.add(null);
 477  0
         currentseq.add(null);
 478  
 
 479  
         try {
 480  0
             MyTokenizer st = new MyTokenizer(s, " ,\t,*,[,],.,:,=,/,\\,",
 481  
                     MyTokenizer.PAREN_EXPR_STRING_SEPARATOR);
 482  
 
 483  0
             while (st.hasMoreTokens()) {
 484  0
                 token = st.nextToken();
 485  
 
 486  0
                 if (" ".equals(token) || "\t".equals(token)) {
 487  0
                     if (currentseq == null) {
 488  0
                         if (varname != null && fname == null) {
 489  0
                             varname.append(token);
 490  
                         }
 491  
                     }
 492  0
                 } else if ("[".equals(token)) {
 493  0
                     if (mustBePre) {
 494  0
                         String msg = "parsing.error.message.pred-unqualified";
 495  0
                         throw new ParseException(Translator.localize(msg),
 496  
                                 st.getTokenIndex());
 497  
                     }
 498  0
                     mustBeSeq = true;
 499  
 
 500  0
                     if (guard != null) {
 501  0
                         String msg = "parsing.error.message.several-specs";
 502  0
                         throw new ParseException(Translator.localize(msg),
 503  
                                 st.getTokenIndex());
 504  
                     }
 505  
 
 506  0
                     guard = new StringBuilder();
 507  
                     while (true) {
 508  0
                         token = st.nextToken();
 509  0
                         if ("]".equals(token)) {
 510  0
                             break;
 511  
                         }
 512  0
                         guard.append(token);
 513  
                     }
 514  0
                 } else if ("*".equals(token)) {
 515  0
                     if (mustBePre) {
 516  0
                         String msg = "parsing.error.message.pred-unqualified";
 517  0
                         throw new ParseException(Translator.localize(msg),
 518  
                                 st.getTokenIndex());
 519  
                     }
 520  0
                     mustBeSeq = true;
 521  
 
 522  0
                     if (currentseq != null) {
 523  0
                         iterative = true;
 524  
                     }
 525  0
                 } else if (".".equals(token)) {
 526  0
                     if (currentseq == null) {
 527  0
                         String msg = "parsing.error.message.unexpected-dot";
 528  0
                         throw new ParseException(Translator.localize(msg),
 529  
                                 st.getTokenIndex());
 530  
                     }
 531  0
                     if (currentseq.get(currentseq.size() - 2) != null
 532  
                             || currentseq.get(currentseq.size() - 1) != null) {
 533  0
                         currentseq.add(null);
 534  0
                         currentseq.add(null);
 535  
                     }
 536  0
                 } else if (":".equals(token)) {
 537  0
                     if (st.hasMoreTokens()) {
 538  0
                         String t = st.nextToken();
 539  0
                         if ("=".equals(t)) {
 540  0
                             st.putToken(":=");
 541  0
                             continue;
 542  
                         }
 543  0
                         st.putToken(t);
 544  
                     }
 545  
 
 546  0
                     if (mustBePre) {
 547  0
                         String msg = "parsing.error.message.pred-colon";
 548  0
                         throw new ParseException(Translator.localize(msg),
 549  
                                 st.getTokenIndex());
 550  
                     }
 551  
 
 552  0
                     if (currentseq != null) {
 553  0
                         if (currentseq.size() > 2
 554  
                             && currentseq.get(currentseq.size() - 2) == null
 555  
                             && currentseq.get(currentseq.size() - 1) == null) {
 556  0
                             currentseq.remove(currentseq.size() - 1);
 557  0
                             currentseq.remove(currentseq.size() - 1);
 558  
                         }
 559  
 
 560  0
                         seqno = currentseq;
 561  0
                         currentseq = null;
 562  0
                         mayDeleteExpr = true;
 563  
                     }
 564  0
                 } else if ("/".equals(token)) {
 565  0
                     if (st.hasMoreTokens()) {
 566  0
                         String t = st.nextToken();
 567  0
                         if ("/".equals(t)) {
 568  0
                             st.putToken("//");
 569  0
                             continue;
 570  
                         }
 571  0
                         st.putToken(t);
 572  
                     }
 573  
 
 574  0
                     if (mustBeSeq) {
 575  0
                         String msg = "parsing.error.message.sequence-slash";
 576  0
                         throw new ParseException(Translator.localize(msg),
 577  
                                 st.getTokenIndex());
 578  
                     }
 579  
 
 580  0
                     mustBePre = false;
 581  0
                     mustBeSeq = true;
 582  
 
 583  0
                     if (currentseq.size() > 2
 584  
                             && currentseq.get(currentseq.size() - 2) == null
 585  
                             && currentseq.get(currentseq.size() - 1) == null) {
 586  0
                         currentseq.remove(currentseq.size() - 1);
 587  0
                         currentseq.remove(currentseq.size() - 1);
 588  
                     }
 589  
 
 590  0
                     if (currentseq.get(currentseq.size() - 2) != null
 591  
                             || currentseq.get(currentseq.size() - 1) != null) {
 592  
 
 593  0
                         predecessors.add(currentseq);
 594  
 
 595  0
                         currentseq = new ArrayList<Integer>();
 596  0
                         currentseq.add(null);
 597  0
                         currentseq.add(null);
 598  
                     }
 599  0
                     hasPredecessors = true;
 600  0
                 } else if ("//".equals(token)) {
 601  0
                     if (mustBePre) {
 602  0
                         String msg = "parsing.error.message.pred-parallelized";
 603  0
                         throw new ParseException(Translator.localize(msg),
 604  
                                 st.getTokenIndex());
 605  
                     }
 606  0
                     mustBeSeq = true;
 607  
 
 608  0
                     if (currentseq != null) {
 609  0
                         parallell = true;
 610  
                     }
 611  0
                 } else if (",".equals(token)) {
 612  0
                     if (currentseq != null) {
 613  0
                         if (mustBeSeq) {
 614  0
                             String msg = "parsing.error.message.many-numbers";
 615  0
                             throw new ParseException(Translator.localize(msg),
 616  
                                     st.getTokenIndex());
 617  
                         }
 618  0
                         mustBePre = true;
 619  
 
 620  0
                         if (currentseq.size() > 2
 621  
                             && currentseq.get(currentseq.size() - 2) == null
 622  
                             && currentseq.get(currentseq.size() - 1) == null) {
 623  
 
 624  0
                             currentseq.remove(currentseq.size() - 1);
 625  0
                             currentseq.remove(currentseq.size() - 1);
 626  
                         }
 627  
 
 628  0
                         if (currentseq.get(currentseq.size() - 2) != null
 629  
                             || currentseq.get(currentseq.size() - 1) != null) {
 630  
 
 631  0
                             predecessors.add(currentseq);
 632  
 
 633  0
                             currentseq = new ArrayList<Integer>();
 634  0
                             currentseq.add(null);
 635  0
                             currentseq.add(null);
 636  
                         }
 637  0
                         hasPredecessors = true;
 638  
                     } else {
 639  0
                         if (varname == null && fname != null) {
 640  0
                             varname = new StringBuilder(fname + token);
 641  0
                             fname = null;
 642  0
                         } else if (varname != null && fname == null) {
 643  0
                             varname.append(token);
 644  
                         } else {
 645  0
                             String msg = "parsing.error.message.found-comma";
 646  0
                             throw new ParseException(
 647  
                                     Translator.localize(msg),
 648  
                                     st.getTokenIndex());
 649  
                         }
 650  
                     }
 651  0
                 } else if ("=".equals(token) || ":=".equals(token)) {
 652  0
                     if (currentseq == null) {
 653  0
                         if (varname == null) {
 654  0
                             varname = new StringBuilder(fname);
 655  0
                             fname = "";
 656  
                         } else {
 657  0
                             fname = "";
 658  
                         }
 659  
                     }
 660  0
                 } else if (currentseq == null) {
 661  0
                     if (paramExpr == null && token.charAt(0) == '(') {
 662  0
                         if (token.charAt(token.length() - 1) != ')') {
 663  0
                             String msg =
 664  
                                 "parsing.error.message.malformed-parameters";
 665  0
                             throw new ParseException(Translator.localize(msg),
 666  
                                     st.getTokenIndex());
 667  
                         }
 668  0
                         if (fname == null || "".equals(fname)) {
 669  0
                             String msg =
 670  
                                 "parsing.error.message.function-not-found";
 671  0
                             throw new ParseException(Translator.localize(msg),
 672  
                                     st.getTokenIndex());
 673  
                         }
 674  0
                         if (varname == null) {
 675  0
                             varname = new StringBuilder();
 676  
                         }
 677  0
                         paramExpr = token.substring(1, token.length() - 1);
 678  0
                     } else if (varname != null && fname == null) {
 679  0
                         varname.append(token);
 680  0
                     } else if (fname == null || fname.length() == 0) {
 681  0
                         fname = token;
 682  
                     } else {
 683  0
                         String msg = "parsing.error.message.unexpected-token";
 684  0
                         Object[] parseExcArgs = {token};
 685  0
                         throw new ParseException(
 686  
                                 Translator.localize(msg, parseExcArgs),
 687  
                                 st.getTokenIndex());
 688  
                     }
 689  
                 } else {
 690  0
                     boolean hasVal =
 691  
                         currentseq.get(currentseq.size() - 2) != null;
 692  0
                     boolean hasOrd =
 693  
                         currentseq.get(currentseq.size() - 1) != null;
 694  0
                     boolean assigned = false;
 695  0
                     int bp = findMsgOrderBreak(token);
 696  
 
 697  0
                     if (!hasVal && !assigned && bp == token.length()) {
 698  
                         try {
 699  0
                             currentseq.set(
 700  
                                 currentseq.size() - 2, Integer.valueOf(
 701  
                                     token));
 702  0
                             assigned = true;
 703  0
                         } catch (NumberFormatException nfe) { }
 704  
                     }
 705  
 
 706  0
                     if (!hasOrd && !assigned && bp == 0) {
 707  
                         try {
 708  0
                             currentseq.set(
 709  
                                 currentseq.size() - 1, Integer.valueOf(
 710  
                                     parseMsgOrder(token)));
 711  0
                             assigned = true;
 712  0
                         } catch (NumberFormatException nfe) { }
 713  
                     }
 714  
 
 715  0
                     if (!hasVal && !hasOrd && !assigned && bp > 0
 716  
                             && bp < token.length()) {
 717  
                         Integer nbr, ord;
 718  
                         try {
 719  0
                             nbr = Integer.valueOf(token.substring(0, bp));
 720  0
                             ord = Integer.valueOf(
 721  
                                     parseMsgOrder(token.substring(bp)));
 722  0
                             currentseq.set(currentseq.size() - 2, nbr);
 723  0
                             currentseq.set(currentseq.size() - 1, ord);
 724  0
                             assigned = true;
 725  0
                         } catch (NumberFormatException nfe) { }
 726  
                     }
 727  
 
 728  0
                     if (!assigned) {
 729  0
                         String msg = "parsing.error.message.unexpected-token";
 730  0
                         Object[] parseExcArgs = {token};
 731  0
                         throw new ParseException(
 732  
                                 Translator.localize(msg, parseExcArgs),
 733  
                                 st.getTokenIndex());
 734  
                     }
 735  0
                 }
 736  
             }
 737  0
         } catch (NoSuchElementException nsee) {
 738  0
             String msg = "parsing.error.message.unexpected-end-message";
 739  0
             throw new ParseException(Translator.localize(msg), s.length());
 740  0
         } catch (ParseException pre) {
 741  0
             throw pre;
 742  0
         }
 743  
 
 744  0
         List<String> args = parseArguments(paramExpr, mayDeleteExpr);
 745  
 
 746  0
         printDebugInfo(s, fname, guard, paramExpr, varname, predecessors,
 747  
                 seqno, parallell, iterative);
 748  
         
 749  
         /* Now apply the changes to the model: */
 750  
 
 751  0
         buildAction(umlMessage);
 752  
 
 753  0
         handleGuard(umlMessage, guard, parallell, iterative);
 754  
 
 755  0
         fname = fillBlankFunctionName(umlMessage, fname, mayDeleteExpr);
 756  
 
 757  0
         varname = fillBlankVariableName(umlMessage, varname, mayDeleteExpr);
 758  
 
 759  0
         refindOperation = handleFunctionName(umlMessage, fname, varname,
 760  
                 refindOperation);
 761  
 
 762  0
         refindOperation = handleArguments(umlMessage, args, refindOperation);
 763  
 
 764  0
         refindOperation = handleSequenceNumber(umlMessage, seqno,
 765  
                 refindOperation);
 766  
 
 767  0
         handleOperation(umlMessage, fname, refindOperation);
 768  
 
 769  0
         handlePredecessors(umlMessage, predecessors, hasPredecessors);
 770  0
     }
 771  
 
 772  
     private void printDebugInfo(String s, String fname, StringBuilder guard,
 773  
             String paramExpr, StringBuilder varname, List<List> predecessors,
 774  
             List<Integer> seqno, boolean parallell, boolean iterative) {
 775  0
         if (LOG.isDebugEnabled()) {
 776  0
             StringBuffer buf = new StringBuffer();
 777  0
             buf.append("ParseMessage: " + s + "\n");
 778  0
             buf.append("Message: ");
 779  0
             for (int i = 0; seqno != null && i + 1 < seqno.size(); i += 2) {
 780  0
                 if (i > 0) {
 781  0
                     buf.append(", ");
 782  
                 }
 783  0
                 buf.append(seqno.get(i) + " (" + seqno.get(i + 1) + ")");
 784  
             }
 785  0
             buf.append("\n");
 786  0
             buf.append("predecessors: " + predecessors.size() + "\n");
 787  0
             for (int i = 0; i < predecessors.size(); i++) {
 788  
                 int j;
 789  0
                 List v = predecessors.get(i);
 790  0
                 buf.append("    Predecessor: ");
 791  0
                 for (j = 0; v != null && j + 1 < v.size(); j += 2) {
 792  0
                     if (j > 0) {
 793  0
                         buf.append(", ");
 794  
                     }
 795  0
                     buf.append(v.get(j) + " (" + v.get(j + 1) + ")");
 796  
                 }
 797  
             }
 798  0
             buf.append("guard: " + guard + " it: " + iterative + " pl: "
 799  
                     + parallell + "\n");
 800  0
             buf.append(varname + " := " + fname + " ( " + paramExpr + " )"
 801  
                     + "\n");
 802  0
             LOG.debug(buf);
 803  
         }
 804  0
     }
 805  
 
 806  
     /**
 807  
      * @param paramExpr
 808  
      * @param mayDeleteExpr
 809  
      * @return
 810  
      */
 811  
     protected List<String> parseArguments(String paramExpr,
 812  
             boolean mayDeleteExpr) {
 813  
         String token;
 814  0
         List<String> args = null;
 815  0
         if (paramExpr != null) {
 816  0
             MyTokenizer st = new MyTokenizer(paramExpr, "\\,",
 817  
                     parameterCustomSep);
 818  0
             args = new ArrayList<String>();
 819  0
             while (st.hasMoreTokens()) {
 820  0
                 token = st.nextToken();
 821  
 
 822  0
                 if (",".equals(token)) {
 823  0
                     if (args.size() == 0) {
 824  0
                         args.add(null);
 825  
                     }
 826  0
                     args.add(null);
 827  
                 } else {
 828  0
                     if (args.size() == 0) {
 829  0
                         if (token.trim().length() == 0) {
 830  0
                             continue;
 831  
                         }
 832  0
                         args.add(null);
 833  
                     }
 834  0
                     String arg = args.get(args.size() - 1);
 835  0
                     if (arg != null) {
 836  0
                         arg = arg + token;
 837  
                     } else {
 838  0
                         arg = token;
 839  
                     }
 840  0
                     args.set(args.size() - 1, arg);
 841  0
                 }
 842  
             }
 843  0
         } else if (mayDeleteExpr) {
 844  0
             args = new ArrayList<String>();
 845  
         }
 846  0
         return args;
 847  
     }
 848  
 
 849  
     /**
 850  
      * Set the predecessors of the given Message.
 851  
      * 
 852  
      * @param umlMessage the given UML Message object to be adapted
 853  
      * @param predecessors the given predecessors as parsed
 854  
      * @param hasPredecessors true if there are some, if false we do nothing
 855  
      * @throws ParseException if something is wrong with the predecessor text
 856  
      */
 857  
     protected void handlePredecessors(Object umlMessage,
 858  
             List<List> predecessors, boolean hasPredecessors)
 859  
         throws ParseException {
 860  
 
 861  
         // Predecessors used to be not implemented, because it
 862  
         // caused some problems that I've not found an easy way to handle yet,
 863  
         // d00mst. The specific problem is that the notation currently is
 864  
         // ambiguous on second message after a thread split.
 865  
         // Why not implement it anyway? d00mst
 866  
         // TODO: Document this ambiguity and the choice made.
 867  0
         if (hasPredecessors) {
 868  0
             Collection roots =
 869  
                 findCandidateRoots(
 870  
                         Model.getFacade().getMessages(
 871  
                                 Model.getFacade().getInteraction(umlMessage)),
 872  
                                 null,
 873  
                                 null);
 874  0
             List<Object> pre = new ArrayList<Object>();
 875  
 
 876  
         predfor:
 877  0
             for (int i = 0; i < predecessors.size(); i++) {
 878  0
                 for (Object root : roots) {
 879  0
                     Object msg =
 880  
                         walkTree(root, predecessors.get(i));
 881  0
                     if (msg != null && msg != umlMessage) {
 882  0
                         if (isBadPreMsg(umlMessage, msg)) {
 883  0
                             String parseMsg = "parsing.error.message.one-pred";
 884  0
                             throw new ParseException(
 885  
                                     Translator.localize(parseMsg), 0);
 886  
                         }
 887  0
                         pre.add(msg);
 888  0
                         continue predfor;
 889  
                     }
 890  0
                 }
 891  0
                 String parseMsg = "parsing.error.message.pred-not-found";
 892  0
                 throw new ParseException(Translator.localize(parseMsg), 0);
 893  
             }
 894  0
             MsgPtr ptr = new MsgPtr();
 895  0
             recCountPredecessors(umlMessage, ptr);
 896  0
             if (ptr.message != null && !pre.contains(ptr.message)) {
 897  0
                 pre.add(ptr.message);
 898  
             }
 899  0
             Model.getCollaborationsHelper().setPredecessors(umlMessage, pre);
 900  
         }
 901  0
     }
 902  
 
 903  
     /**
 904  
      * Update the model with the operation name. <p>
 905  
      * 
 906  
      * The given operation name is located on the receiver of the given message.
 907  
      * If an operation with the given name 
 908  
      * and a matching number of arguments is located,
 909  
      * then the CallAction of the message is adapted accordingly.
 910  
      *   
 911  
      * @param umlMessage the message of which the CallAction is to be adapted
 912  
      * @param fname the name of the operation to be used
 913  
      * @param refindOperation true if we have to set the operation 
 914  
      * of the CallAction
 915  
      * @throws ParseException if the operation syntax can not be parsed
 916  
      */
 917  
     protected void handleOperation(Object umlMessage, String fname,
 918  
             boolean refindOperation) throws ParseException {
 919  0
         if (fname != null && refindOperation) {
 920  0
             Object role = Model.getFacade().getReceiver(umlMessage);
 921  0
             List ops =
 922  
                 getOperation(
 923  
                     Model.getFacade().getBases(role),
 924  
                     fname.trim(),
 925  
                     Model.getFacade().getActualArguments(
 926  
                             Model.getFacade().getAction(umlMessage)).size());
 927  
 
 928  0
             Object callAction = Model.getFacade().getAction(umlMessage);
 929  0
             if (Model.getFacade().isACallAction(callAction)) {
 930  0
                 if (ops.size() > 0) {
 931  
                     // If there are more than one suitable operation,
 932  
                     // then we pick the first one.
 933  0
                     Model.getCommonBehaviorHelper().setOperation(callAction,
 934  
                             ops.get(0));
 935  
                 } else {
 936  0
                     Model.getCommonBehaviorHelper().setOperation(
 937  
                             callAction, null);
 938  
                 }
 939  
             }
 940  
         }
 941  0
     }
 942  
 
 943  
     /**
 944  
      * @param umlMessage
 945  
      * @param seqno
 946  
      * @param refindOperation
 947  
      * @return
 948  
      * @throws ParseException
 949  
      */
 950  
     protected boolean handleSequenceNumber(Object umlMessage,
 951  
             List<Integer> seqno, boolean refindOperation) throws ParseException {
 952  
         int i;
 953  0
         if (seqno != null) {
 954  
             Object/* MMessage */root;
 955  
             // Find the preceding message, if any, on either end of the
 956  
             // association.
 957  0
             StringBuilder pname = new StringBuilder();
 958  0
             StringBuilder mname = new StringBuilder();
 959  0
             String gname = generateMessageNumber(umlMessage);
 960  0
             boolean swapRoles = false;
 961  0
             int majval = 0;
 962  0
             if (seqno.get(seqno.size() - 2) != null) {
 963  0
                 majval =
 964  
                     Math.max((seqno.get(seqno.size() - 2)).intValue()
 965  
                             - 1,
 966  
                             0);
 967  
             }
 968  0
             int minval = 0;
 969  0
             if (seqno.get(seqno.size() - 1) != null) {
 970  0
                 minval =
 971  
                     Math.max((seqno.get(seqno.size() - 1)).intValue(),
 972  
                             0);
 973  
             }
 974  
 
 975  0
             for (i = 0; i + 1 < seqno.size(); i += 2) {
 976  0
                 int bv = 1;
 977  0
                 if (seqno.get(i) != null) {
 978  0
                     bv = Math.max((seqno.get(i)).intValue(), 1);
 979  
                 }
 980  
 
 981  0
                 int sv = 0;
 982  0
                 if (seqno.get(i + 1) != null) {
 983  0
                     sv = Math.max((seqno.get(i + 1)).intValue(), 0);
 984  
                 }
 985  
 
 986  0
                 if (i > 0) {
 987  0
                     mname.append(".");
 988  
                 }
 989  0
                 mname.append(Integer.toString(bv) + (char) ('a' + sv));
 990  
 
 991  0
                 if (i + 3 < seqno.size()) {
 992  0
                     if (i > 0) {
 993  0
                         pname.append(".");
 994  
                     }
 995  0
                     pname.append(Integer.toString(bv) + (char) ('a' + sv));
 996  
                 }
 997  
             }
 998  
 
 999  0
             root = null;
 1000  0
             if (pname.length() > 0) {
 1001  0
                 root = findMsg(Model.getFacade().getSender(umlMessage), 
 1002  
                         pname.toString());
 1003  0
                 if (root == null) {
 1004  0
                     root = findMsg(Model.getFacade().getReceiver(umlMessage), 
 1005  
                             pname.toString());
 1006  0
                     if (root != null) {
 1007  0
                         swapRoles = true;
 1008  
                     }
 1009  
                 }
 1010  0
             } else if (!hasMsgWithActivator(Model.getFacade().getSender(umlMessage),
 1011  
                     null)
 1012  
                     && hasMsgWithActivator(Model.getFacade().getReceiver(umlMessage),
 1013  
                             null)) {
 1014  0
                 swapRoles = true;
 1015  
             }
 1016  
 
 1017  0
             if (compareMsgNumbers(mname.toString(), gname.toString())) {
 1018  
                 // Do nothing
 1019  0
             } else if (isMsgNumberStartOf(gname.toString(), mname.toString())) {
 1020  0
                 String msg = "parsing.error.message.subtree-rooted-self";
 1021  0
                 throw new ParseException(Translator.localize(msg), 0);
 1022  0
             } else if (Model.getFacade().getPredecessors(umlMessage).size() > 1
 1023  
                     && Model.getFacade().getSuccessors(umlMessage).size() > 1) {
 1024  0
                 String msg = "parsing.error.message.start-end-many-threads";
 1025  0
                 throw new ParseException(Translator.localize(msg), 0);
 1026  0
             } else if (root == null && pname.length() > 0) {
 1027  0
                 String msg = "parsing.error.message.activator-not-found";
 1028  0
                 throw new ParseException(Translator.localize(msg), 0);
 1029  0
             } else if (swapRoles
 1030  
                     && Model.getFacade().getActivatedMessages(umlMessage).size() > 0
 1031  
                     && (Model.getFacade().getSender(umlMessage)
 1032  
                             != Model.getFacade().getReceiver(umlMessage))) {
 1033  0
                 String msg = "parsing.error.message.reverse-direction-message";
 1034  0
                 throw new ParseException(Translator.localize(msg), 0);
 1035  
             } else {
 1036  
                 /* Disconnect the message from the call graph
 1037  
                  * Make copies of returned live collections
 1038  
                  * since we're modifying
 1039  
                  */
 1040  0
                 Collection c = new ArrayList(
 1041  
                         Model.getFacade().getPredecessors(umlMessage));
 1042  0
                 Collection c2 = new ArrayList(
 1043  
                         Model.getFacade().getSuccessors(umlMessage));
 1044  
                 Iterator it;
 1045  
 
 1046  0
                 it = c2.iterator();
 1047  0
                 while (it.hasNext()) {
 1048  0
                     Model.getCollaborationsHelper().removeSuccessor(umlMessage,
 1049  
                             it.next());
 1050  
                 }
 1051  
 
 1052  0
                 it = c.iterator();
 1053  0
                 while (it.hasNext()) {
 1054  0
                     Iterator it2 = c2.iterator();
 1055  0
                     Object pre = /* (MMessage) */it.next();
 1056  0
                     Model.getCollaborationsHelper().removePredecessor(umlMessage, pre);
 1057  0
                     while (it2.hasNext()) {
 1058  0
                         Model.getCollaborationsHelper().addPredecessor(
 1059  
                                 it2.next(), pre);
 1060  
                     }
 1061  0
                 }
 1062  
 
 1063  
                 // Connect the message at a new spot
 1064  0
                 Model.getCollaborationsHelper().setActivator(umlMessage, root);
 1065  0
                 if (swapRoles) {
 1066  0
                     Object/* MClassifierRole */r =
 1067  
                         Model.getFacade().getSender(umlMessage);
 1068  0
                     Model.getCollaborationsHelper().setSender(umlMessage,
 1069  
                             Model.getFacade().getReceiver(umlMessage));
 1070  0
                     Model.getCommonBehaviorHelper().setReceiver(umlMessage, r);
 1071  
                 }
 1072  
 
 1073  0
                 if (root == null) {
 1074  0
                     c =
 1075  
                         filterWithActivator(
 1076  
                                 Model.getFacade().getSentMessages(
 1077  
                                         Model.getFacade().getSender(umlMessage)),
 1078  
                                         null);
 1079  
                 } else {
 1080  0
                     c = Model.getFacade().getActivatedMessages(root);
 1081  
                 }
 1082  0
                 c2 = findCandidateRoots(c, root, umlMessage);
 1083  0
                 it = c2.iterator();
 1084  
                 // If c2 is empty, then we're done (or there is a
 1085  
                 // cycle in the message graph, which would be bad) If
 1086  
                 // c2 has more than one element, then the model is
 1087  
                 // crappy, but we'll just use one of them anyway
 1088  0
                 if (majval <= 0) {
 1089  0
                     while (it.hasNext()) {
 1090  0
                         Model.getCollaborationsHelper().addSuccessor(umlMessage,
 1091  
                                 /* (MMessage) */it.next());
 1092  
                     }
 1093  0
                 } else if (it.hasNext()) {
 1094  0
                     Object/* MMessage */pre =
 1095  
                         walk(/* (MMessage) */it.next(), majval - 1, false);
 1096  0
                     Object/* MMessage */post = successor(pre, minval);
 1097  0
                     if (post != null) {
 1098  0
                         Model.getCollaborationsHelper()
 1099  
                             .removePredecessor(post, pre);
 1100  0
                         Model.getCollaborationsHelper()
 1101  
                             .addPredecessor(post, umlMessage);
 1102  
                     }
 1103  0
                     insertSuccessor(pre, umlMessage, minval);
 1104  
                 }
 1105  0
                 refindOperation = true;
 1106  
             }
 1107  
         }
 1108  0
         return refindOperation;
 1109  
     }
 1110  
 
 1111  
     /**
 1112  
      * @param umlMessage
 1113  
      * @param args
 1114  
      * @param refindOperation
 1115  
      * @return
 1116  
      */
 1117  
     protected boolean handleArguments(Object umlMessage, List<String> args,
 1118  
             boolean refindOperation) {
 1119  0
         if (args != null) {
 1120  0
             Collection c = new ArrayList(
 1121  
                     Model.getFacade().getActualArguments(
 1122  
                             Model.getFacade().getAction(umlMessage)));
 1123  0
             Iterator it = c.iterator();
 1124  
             int ii;
 1125  0
             for (ii = 0; ii < args.size(); ii++) {
 1126  0
                 Object umlArgument = (it.hasNext() ? it.next() : null);
 1127  0
                 if (umlArgument == null) {
 1128  0
                     umlArgument = Model.getCommonBehaviorFactory()
 1129  
                         .createArgument();
 1130  0
                     Model.getCommonBehaviorHelper().addActualArgument(
 1131  
                             Model.getFacade().getAction(umlMessage), umlArgument);
 1132  0
                     refindOperation = true;
 1133  
                 }
 1134  0
                 if (Model.getFacade().getValue(umlArgument) == null
 1135  
                         || !args.get(ii).equals(
 1136  
                                 Model.getFacade().getBody(
 1137  
                                         Model.getFacade().getValue(umlArgument)))) {
 1138  0
                     String value = (args.get(ii) != null ? args.get(ii)
 1139  
                             : "");
 1140  0
                     Object umlExpression =
 1141  
                         Model.getDataTypesFactory().createExpression(
 1142  
                                 getExpressionLanguage(),
 1143  
                                 value.trim());
 1144  0
                     Model.getCommonBehaviorHelper().setValue(umlArgument, umlExpression);
 1145  
                 }
 1146  
             }
 1147  
 
 1148  0
             while (it.hasNext()) {
 1149  0
                 Model.getCommonBehaviorHelper()
 1150  
                     .removeActualArgument(Model.getFacade().getAction(umlMessage),
 1151  
                         it.next());
 1152  0
                 refindOperation = true;
 1153  
             }
 1154  
         }
 1155  0
         return refindOperation;
 1156  
     }
 1157  
 
 1158  
     /**
 1159  
      * Store the given function name and return variable name 
 1160  
      * in the script of the action of the given message. <p>
 1161  
      * 
 1162  
      * Constraint: the given Message shall have an Action.
 1163  
      * 
 1164  
      * @param umlMessage the given UML Message object to adapt
 1165  
      * @param fname the name of the function
 1166  
      * @param varname the return variable name
 1167  
      * @param refindOperation if false, then we may return true or false. 
 1168  
      * If true, we return true.
 1169  
      * @return true if we stored the fname and varname 
 1170  
      * in the Action of the Message
 1171  
      */
 1172  
     protected boolean handleFunctionName(Object umlMessage, String fname,
 1173  
             StringBuilder varname, boolean refindOperation) {
 1174  0
         if (fname != null) {
 1175  0
             String expr = fname.trim();
 1176  0
             if (varname.length() > 0) {
 1177  0
                 expr = varname.toString().trim() + " := " + expr;
 1178  
             }
 1179  
 
 1180  0
             Object action = Model.getFacade().getAction(umlMessage);
 1181  0
             assert action != null;
 1182  0
             Object script = Model.getFacade().getScript(action);
 1183  0
             if (script == null
 1184  
                     || !expr.equals(Model.getFacade().getBody(script))) {
 1185  0
                 Object newActionExpression =
 1186  
                     Model.getDataTypesFactory()
 1187  
                         .createActionExpression(
 1188  
                             getExpressionLanguage(),
 1189  
                             expr.trim());
 1190  0
                 Model.getCommonBehaviorHelper().setScript(
 1191  
                         action, newActionExpression);
 1192  0
                 refindOperation = true;
 1193  
             }
 1194  
         }
 1195  0
         return refindOperation;
 1196  
     }
 1197  
 
 1198  
     /**
 1199  
      * Fill in the variable name if it is blank.  <p>
 1200  
      * The variable name is the part in front of the assignment operator.
 1201  
      * 
 1202  
      * @param umlMessage the given message to fill the variable name for
 1203  
      * @param varname if null, then we get the variable name from the model. 
 1204  
      * @param mayDeleteExpr if true, then we may delete the variable, 
 1205  
      * and hence we return an empty string
 1206  
      * @return the original variable name, or if it was null, 
 1207  
      * a variable name generated from the model
 1208  
      */
 1209  
     protected StringBuilder fillBlankVariableName(Object umlMessage,
 1210  
             StringBuilder varname, boolean mayDeleteExpr) {
 1211  
         /* If no variable name was given, then retain the one in the model. */
 1212  0
         if (varname == null) {
 1213  0
             Object script = Model.getFacade().getScript(
 1214  
                     Model.getFacade().getAction(umlMessage));
 1215  0
             if (!mayDeleteExpr && script != null) {
 1216  0
                 String body =
 1217  
                     (String) Model.getFacade().getBody(script);
 1218  0
                 int idx = body.indexOf(":=");
 1219  0
                 if (idx < 0) {
 1220  0
                     idx = body.indexOf("=");
 1221  
                 }
 1222  
 
 1223  0
                 if (idx >= 0) {
 1224  0
                     varname = new StringBuilder(body.substring(0, idx));
 1225  
                 } else {
 1226  0
                     varname = new StringBuilder();
 1227  
                 }
 1228  0
             } else {
 1229  0
                 varname = new StringBuilder();
 1230  
             }
 1231  
         }
 1232  0
         return varname;
 1233  
     }
 1234  
 
 1235  
     /**
 1236  
      * Fill in the function name if it is blank. <p>
 1237  
      * 
 1238  
      * The fname is the part of the script after the assignment operator.
 1239  
      * 
 1240  
      * @param umlMessage the given message to fill the fname for
 1241  
      * @param fname if null, then we get the fname from the model. 
 1242  
      * @param mayDeleteExpr if true, then we may delete the function, 
 1243  
      * and hence we return an empty string
 1244  
      * @return the original fname, or if it was null, 
 1245  
      * a fname generated from the model
 1246  
      */
 1247  
     protected String fillBlankFunctionName(Object umlMessage, String fname,
 1248  
             boolean mayDeleteExpr) {
 1249  
         /* If no function-name was given, then retain the one in the model. */
 1250  0
         if (fname == null) {
 1251  0
             Object script = Model.getFacade().getScript(
 1252  
                     Model.getFacade().getAction(umlMessage));
 1253  0
             if (!mayDeleteExpr && script != null) {
 1254  0
                 String body =
 1255  
                     (String) Model.getFacade().getBody(script);
 1256  
 
 1257  0
                 int idx = body.indexOf(":=");
 1258  0
                 if (idx >= 0) {
 1259  0
                     idx++;
 1260  
                 } else {
 1261  0
                     idx = body.indexOf("=");
 1262  
                 }
 1263  
 
 1264  0
                 if (idx >= 0) {
 1265  0
                     fname = body.substring(idx + 1);
 1266  
                 } else {
 1267  0
                     fname = body;
 1268  
                 }
 1269  0
             } else {
 1270  0
                 fname = "";
 1271  
             }
 1272  
         }
 1273  0
         return fname;
 1274  
     }
 1275  
 
 1276  
     /**
 1277  
      * Store the parsed guard in the UML objects related to the given Message. 
 1278  
      * 
 1279  
      * @param umlMessage the UML Message object
 1280  
      * @param guard the guard expression string
 1281  
      * @param parallell true if parallel execution was indicated
 1282  
      * @param iterative true if this is an iterative expression, 
 1283  
      * as opposed to a condition
 1284  
      */
 1285  
     protected void handleGuard(Object umlMessage, StringBuilder guard,
 1286  
             boolean parallell, boolean iterative) {
 1287  
         /* Store the guard, i.e. condition or iteration expression, 
 1288  
          * in the recurrence field of the Action: */
 1289  0
         if (guard != null) {
 1290  0
             guard = new StringBuilder("[" + guard.toString().trim() + "]");
 1291  0
             if (iterative) {
 1292  0
                 if (parallell) {
 1293  0
                     guard = guard.insert(0, "*//");
 1294  
                 } else {
 1295  0
                     guard = guard.insert(0, "*");
 1296  
                 }
 1297  
             }
 1298  0
             Object expr =
 1299  
                 Model.getDataTypesFactory().createIterationExpression(
 1300  
                         getExpressionLanguage(), guard.toString());
 1301  0
             Model.getCommonBehaviorHelper().setRecurrence(
 1302  
                     Model.getFacade().getAction(umlMessage), expr);
 1303  
         }
 1304  0
     }
 1305  
 
 1306  
     /**
 1307  
      * Build a CallAction for the given UML Message
 1308  
      * if it did not have an Action yet.
 1309  
      * 
 1310  
      * @param umlMessage the UML Message object to create an Action for
 1311  
      */
 1312  
     protected void buildAction(Object umlMessage) {
 1313  0
         if (Model.getFacade().getAction(umlMessage) == null) {
 1314  
             /* If there was no Action yet, create a CallAction: */
 1315  0
             Object a = Model.getCommonBehaviorFactory()
 1316  
                 .createCallAction();
 1317  0
             Model.getCoreHelper().addOwnedElement(Model.getFacade().getContext(
 1318  
                     Model.getFacade().getInteraction(umlMessage)), a);
 1319  0
             Model.getCollaborationsHelper().setAction(umlMessage, a);
 1320  
         }
 1321  0
     }
 1322  
 
 1323  
     /**
 1324  
      * TODO: This name of the expression language should be 
 1325  
      * configurable by the user. <p>
 1326  
      * 
 1327  
      * According to the UML standard, 
 1328  
      * the expression language should be the same 
 1329  
      * for all elements in one diagram. <p>
 1330  
      * 
 1331  
      * UML is not a sensible default - usually this is some pseudo-language.
 1332  
      * 
 1333  
      * @return the name of the expression language
 1334  
      */
 1335  
     private String getExpressionLanguage() {
 1336  0
         return "";
 1337  
     }
 1338  
 
 1339  
     /**
 1340  
      * Walks a call tree from a <code>root</code> node 
 1341  
      * following the directions given in <code>path</code>
 1342  
      * to a destination node. If the destination node cannot be reached, then
 1343  
      * null is returned.
 1344  
      *
 1345  
      * @param root The root of the call tree.
 1346  
      * @param path The path to walk in the call tree.
 1347  
      * @return The message at the end of path, or <code>null</code>.
 1348  
      */
 1349  
     private Object walkTree(Object root, List path) {
 1350  
         int i;
 1351  0
         for (i = 0; i + 1 < path.size(); i += 2) {
 1352  0
             int bv = 0;
 1353  0
             if (path.get(i) != null) {
 1354  0
                 bv = Math.max(((Integer) path.get(i)).intValue() - 1, 0);
 1355  
             }
 1356  
     
 1357  0
             int sv = 0;
 1358  0
             if (path.get(i + 1) != null) {
 1359  0
                 sv = Math.max(((Integer) path.get(i + 1)).intValue(), 0);
 1360  
             }
 1361  
     
 1362  0
             root = walk(root, bv - 1, true);
 1363  0
             if (root == null) {
 1364  0
                 return null;
 1365  
             }
 1366  0
             if (bv > 0) {
 1367  0
                 root = successor(root, sv);
 1368  0
                 if (root == null) {
 1369  0
                     return null;
 1370  
                 }
 1371  
             }
 1372  0
             if (i + 3 < path.size()) {
 1373  0
                 Iterator it =
 1374  
                     findCandidateRoots(
 1375  
                             Model.getFacade().getActivatedMessages(root),
 1376  
                             root,
 1377  
                             null).iterator();
 1378  
                 // Things are strange if there are more than one candidate root,
 1379  
                 // it has no obvious interpretation in terms of a call tree.
 1380  0
                 if (!it.hasNext()) {
 1381  0
                     return null;
 1382  
                 }
 1383  0
                 root = /* (MMessage) */it.next();
 1384  
             }
 1385  
         }
 1386  0
         return root;
 1387  
     }
 1388  
 
 1389  
     /**
 1390  
      * Finds the steps'th successor of r in the sense that it is a successor of
 1391  
      * a successor of r (steps times). The first successor with the same
 1392  
      * activator as r is used in each step. If there are not enough successors,
 1393  
      * then struct determines the result. If struct is true, then null is
 1394  
      * returned, otherwise the last successor found.
 1395  
      */
 1396  
     private Object walk(Object/* MMessage */r, int steps, boolean strict) {
 1397  0
         Object/* MMessage */act = Model.getFacade().getActivator(r);
 1398  0
         while (steps > 0) {
 1399  0
             Iterator it = Model.getFacade().getSuccessors(r).iterator();
 1400  
             do {
 1401  0
                 if (!it.hasNext()) {
 1402  0
                     return (strict ? null : r);
 1403  
                 }
 1404  0
                 r = /* (MMessage) */it.next();
 1405  0
             } while (Model.getFacade().getActivator(r) != act);
 1406  0
             steps--;
 1407  0
         }
 1408  0
         return r;
 1409  
     }
 1410  
 
 1411  
     /**
 1412  
      * Finds the root candidates in a collection c, ie the messages in c that
 1413  
      * has the activator a (may be null) and has no predecessor with the same
 1414  
      * activator. If veto isn't null, then the message in veto will not be
 1415  
      * included in the Collection of candidates.
 1416  
      *
 1417  
      * @param c The collection of UML Message objects.
 1418  
      * @param a The message.
 1419  
      * @param veto The excluded message.
 1420  
      * @return The found roots.
 1421  
      */
 1422  
     private Collection findCandidateRoots(Collection c, Object a, Object veto) {
 1423  0
         List<Object> candidates = new ArrayList<Object>();
 1424  0
         for (Object message : c) {
 1425  0
             if (message == veto) {
 1426  0
                 continue;
 1427  
             }
 1428  0
             if (Model.getFacade().getActivator(message) != a) {
 1429  0
                 continue;
 1430  
             }
 1431  0
             Collection predecessors =
 1432  
                 Model.getFacade().getPredecessors(message);
 1433  0
             boolean isCandidate = true;
 1434  0
             for (Object predecessor : predecessors) {
 1435  0
                 if (Model.getFacade().getActivator(predecessor) == a) {
 1436  0
                     isCandidate = false;
 1437  
                 }
 1438  
             }
 1439  0
             if (isCandidate) {
 1440  0
                 candidates.add(message);
 1441  
             }
 1442  0
         }
 1443  0
         return candidates;
 1444  
     }
 1445  
 
 1446  
     /**
 1447  
      * Finds the steps'th successor of message r in the sense that it is a
 1448  
      * direct successor of r. Returns null if r has fewer successors.
 1449  
      */
 1450  
     private Object successor(Object/* MMessage */r, int steps) {
 1451  0
         Iterator it = Model.getFacade().getSuccessors(r).iterator();
 1452  0
         while (it.hasNext() && steps > 0) {
 1453  0
             it.next();
 1454  0
             steps--;
 1455  
         }
 1456  0
         if (it.hasNext()) {
 1457  0
             return /* (MMessage) */it.next();
 1458  
         }
 1459  0
         return null;
 1460  
     }
 1461  
 
 1462  
     /**
 1463  
      * Compares two message numbers n1, n2 with each other to determine if n1
 1464  
      * specifies a the same position as n2 in a call tree or n1 specifies a
 1465  
      * position that is a father of the position specified by n2.
 1466  
      */
 1467  
     private boolean isMsgNumberStartOf(String n1, String n2) {
 1468  
         int i, j, len, jlen;
 1469  0
         len = n1.length();
 1470  0
         jlen = n2.length();
 1471  0
         i = 0;
 1472  0
         j = 0;
 1473  0
         for (; i < len;) {
 1474  
             int ibv, isv;
 1475  
             int jbv, jsv;
 1476  
     
 1477  0
             ibv = 0;
 1478  0
             for (; i < len; i++) {
 1479  0
                 char c = n1.charAt(i);
 1480  0
                 if (c < '0' || c > '9') {
 1481  0
                     break;
 1482  
                 }
 1483  0
                 ibv *= 10;
 1484  0
                 ibv += c - '0';
 1485  
             }
 1486  0
             isv = 0;
 1487  0
             for (; i < len; i++) {
 1488  0
                 char c = n1.charAt(i);
 1489  0
                 if (c < 'a' || c > 'z') {
 1490  0
                     break;
 1491  
                 }
 1492  0
                 isv *= 26;
 1493  0
                 isv += c - 'a';
 1494  
             }
 1495  
     
 1496  0
             jbv = 0;
 1497  0
             for (; j < jlen; j++) {
 1498  0
                 char c = n2.charAt(j);
 1499  0
                 if (c < '0' || c > '9') {
 1500  0
                     break;
 1501  
                 }
 1502  0
                 jbv *= 10;
 1503  0
                 jbv += c - '0';
 1504  
             }
 1505  0
             jsv = 0;
 1506  0
             for (; j < jlen; j++) {
 1507  0
                 char c = n2.charAt(j);
 1508  0
                 if (c < 'a' || c > 'z') {
 1509  0
                     break;
 1510  
                 }
 1511  0
                 jsv *= 26;
 1512  0
                 jsv += c - 'a';
 1513  
             }
 1514  
     
 1515  0
             if (ibv != jbv || isv != jsv) {
 1516  0
                 return false;
 1517  
             }
 1518  
     
 1519  0
             if (i < len && n1.charAt(i) != '.') {
 1520  0
                 return false;
 1521  
             }
 1522  0
             i++;
 1523  
     
 1524  0
             if (j < jlen && n2.charAt(j) != '.') {
 1525  0
                 return false;
 1526  
             }
 1527  0
             j++;
 1528  0
         }
 1529  0
         return true;
 1530  
     }
 1531  
 
 1532  
     /**
 1533  
      * Compares two message numbers with each other to see if they are equal, in
 1534  
      * the sense that they refer to the same position in a call tree.
 1535  
      *
 1536  
      * @param n1 The first number.
 1537  
      * @param n2 The second number.
 1538  
      * @return <code>true</code> if they are the same.
 1539  
      */
 1540  
     private boolean compareMsgNumbers(String n1, String n2) {
 1541  0
         return isMsgNumberStartOf(n1, n2) && isMsgNumberStartOf(n2, n1);
 1542  
     }
 1543  
 
 1544  
     /**
 1545  
      * Parses a message order specification.
 1546  
      */
 1547  
     private static int parseMsgOrder(String s) {
 1548  
         int i, t;
 1549  0
         int v = 0;
 1550  
     
 1551  0
         t = s.length();
 1552  0
         for (i = 0; i < t; i++) {
 1553  0
             char c = s.charAt(i);
 1554  0
             if (c < 'a' || c > 'z') {
 1555  0
                 throw new NumberFormatException();
 1556  
             }
 1557  0
             v *= 26;
 1558  0
             v += c - 'a';
 1559  
         }
 1560  
     
 1561  0
         return v;
 1562  
     }
 1563  
 
 1564  
     /**
 1565  
      * Finds the message in ClassifierRole r that has the message number written
 1566  
      * in n. If it isn't found, null is returned.
 1567  
      */
 1568  
     private Object findMsg(Object/* MClassifierRole */r, String n) {
 1569  0
         Collection c = Model.getFacade().getReceivedMessages(r);
 1570  0
         Iterator it = c.iterator();
 1571  0
         while (it.hasNext()) {
 1572  0
             Object msg = /* (MMessage) */it.next();
 1573  0
             String gname = generateMessageNumber(msg);
 1574  0
             if (compareMsgNumbers(gname, n)) {
 1575  0
                 return msg;
 1576  
             }
 1577  0
         }
 1578  0
         return null;
 1579  
     }
 1580  
 
 1581  
     /**
 1582  
      * Examines a collection to see if any message has the given message as an
 1583  
      * activator.
 1584  
      *
 1585  
      * @param r
 1586  
      *            MClassifierRole
 1587  
      * @param m
 1588  
      *            MMessage
 1589  
      */
 1590  
     private boolean hasMsgWithActivator(Object r, Object m) {
 1591  0
         Iterator it = Model.getFacade().getSentMessages(r).iterator();
 1592  0
         while (it.hasNext()) {
 1593  0
             if (Model.getFacade().getActivator(it.next()) == m) {
 1594  0
                 return true;
 1595  
             }
 1596  
         }
 1597  0
         return false;
 1598  
     }
 1599  
 
 1600  
     /**
 1601  
      * Examines the call tree from chld to see if ans is an ancestor.
 1602  
      *
 1603  
      * @param ans
 1604  
      *            MMessage
 1605  
      * @param chld
 1606  
      *            MMessage
 1607  
      */
 1608  
     private boolean isBadPreMsg(Object ans, Object chld) {
 1609  0
         while (chld != null) {
 1610  0
             if (ans == chld) {
 1611  0
                 return true;
 1612  
             }
 1613  0
             if (isPredecessorMsg(ans, chld, 100)) {
 1614  0
                 return true;
 1615  
             }
 1616  0
             chld = Model.getFacade().getActivator(chld);
 1617  
         }
 1618  0
         return false;
 1619  
     }
 1620  
 
 1621  
     /**
 1622  
      * Examines the call tree from suc to see if pre is a predecessor. This
 1623  
      * function is recursive and md specifies the maximum level of recursions
 1624  
      * allowed.
 1625  
      *
 1626  
      * @param pre
 1627  
      *            MMessage
 1628  
      * @param suc
 1629  
      *            MMessage
 1630  
      */
 1631  
     private boolean isPredecessorMsg(Object pre, Object suc, int md) {
 1632  0
         Iterator it = Model.getFacade().getPredecessors(suc).iterator();
 1633  0
         while (it.hasNext()) {
 1634  0
             Object m = /* (MMessage) */it.next();
 1635  0
             if (m == pre) {
 1636  0
                 return true;
 1637  
             }
 1638  0
             if (md > 0 && isPredecessorMsg(pre, m, md - 1)) {
 1639  0
                 return true;
 1640  
             }
 1641  0
         }
 1642  0
         return false;
 1643  
     }
 1644  
 
 1645  
     /**
 1646  
      * Finds the messages in Collection c that has message a as activator.
 1647  
      */
 1648  
     private Collection<Object> filterWithActivator(Collection c, 
 1649  
             Object/*MMessage*/a) {
 1650  0
         List<Object> v = new ArrayList<Object>();
 1651  0
         for (Object msg : c) {
 1652  0
             if (Model.getFacade().getActivator(msg) == a) {
 1653  0
                 v.add(msg);
 1654  
             }            
 1655  
         }
 1656  0
         return v;
 1657  
     }
 1658  
 
 1659  
     /**
 1660  
      * Inserts message s as the p'th successor of message m.
 1661  
      *
 1662  
      * @param m
 1663  
      *            MMessage
 1664  
      * @param s
 1665  
      *            MMessage
 1666  
      */
 1667  
     private void insertSuccessor(Object m, Object s, int p) {
 1668  0
         List<Object> successors = 
 1669  
             new ArrayList<Object>(Model.getFacade().getSuccessors(m));
 1670  0
         if (successors.size() > p) {
 1671  0
             successors.add(p, s);
 1672  
         } else {
 1673  0
             successors.add(s);
 1674  
         }
 1675  0
         Model.getCollaborationsHelper().setSuccessors(m, successors);
 1676  0
     }
 1677  
 
 1678  
     /**
 1679  
      * Finds all operations in a given collection of classifiers 
 1680  
      * with the given name 
 1681  
      * and the given number of parameters. 
 1682  
      * If no operation is found, one is created in the first given Classifier. 
 1683  
      * The applicable operations are returned.
 1684  
      *
 1685  
      * @param classifiers the collection of classifiers to search for operations
 1686  
      * @param name the name of the operation to be found
 1687  
      * @param params the number of parameters of the operation to be found
 1688  
      * @return a list of the sought operations
 1689  
      * @throws ParseException if the operation syntax can not be parsed
 1690  
      */
 1691  
     private List getOperation(Collection classifiers, String name, int params) 
 1692  
         throws ParseException {
 1693  0
         List<Object> operations = new ArrayList<Object>();
 1694  
     
 1695  0
         if (name == null || name.length() == 0) {
 1696  0
             return operations;
 1697  
         }
 1698  
     
 1699  0
         for (Object clf : classifiers) {
 1700  0
             Collection oe = Model.getFacade().getFeatures(clf);
 1701  0
             for (Object operation : oe) {
 1702  0
                 if (!(Model.getFacade().isAOperation(operation))) {
 1703  0
                     continue;
 1704  
                 }
 1705  
 
 1706  0
                 if (!name.equals(Model.getFacade().getName(operation))) {
 1707  0
                     continue;
 1708  
                 }
 1709  0
                 if (params != countParameters(operation)) {
 1710  0
                     continue;
 1711  
                 }
 1712  0
                 operations.add(operation);
 1713  
             }
 1714  0
         }
 1715  0
         if (operations.size() > 0) {
 1716  0
             return operations;
 1717  
         }
 1718  
     
 1719  0
         Iterator it = classifiers.iterator();
 1720  0
         if (it.hasNext()) {
 1721  0
             StringBuilder expr = new StringBuilder(name + "(");
 1722  
             int i;
 1723  0
             for (i = 0; i < params; i++) {
 1724  0
                 if (i > 0) {
 1725  0
                     expr.append(", ");
 1726  
                 }
 1727  0
                 expr.append("param" + (i + 1));
 1728  
             }
 1729  0
             expr.append(")");
 1730  
             // Jaap Branderhorst 2002-23-09 added next lines to link
 1731  
             // parameters and operations to the figs that represent
 1732  
             // them
 1733  0
             Object cls = it.next();
 1734  0
             Object returnType =
 1735  
                 ProjectManager.getManager()
 1736  
                         .getCurrentProject().getDefaultReturnType();
 1737  0
             Object op = Model.getCoreFactory().buildOperation(cls, returnType);
 1738  
     
 1739  0
             new OperationNotationUml(op).parseOperation(
 1740  
                         expr.toString(), op);
 1741  0
             operations.add(op);
 1742  
         }
 1743  0
         return operations;
 1744  
     }
 1745  
 
 1746  
     /**
 1747  
      * Counts the number of parameters that are not return values.
 1748  
      */
 1749  
     private int countParameters(Object bf) {
 1750  0
         int count = 0;
 1751  0
         for (Object parameter : Model.getFacade().getParameters(bf)) {
 1752  0
             if (!Model.getFacade().isReturn(parameter)) {
 1753  0
                 count++;
 1754  
             }
 1755  
         }
 1756  0
         return count;
 1757  
     }
 1758  
 
 1759  
     /**
 1760  
      * Recursively count the number of predecessors of the given Message, 
 1761  
      * and return (a pointer to) the first Message in the chain.
 1762  
      * 
 1763  
      * @param umlMessage the UML Message to count the predecessors for
 1764  
      * @param ptr an object to contain the returned first Message
 1765  
      * @return the number of messages in the chain
 1766  
      */
 1767  
     protected int recCountPredecessors(Object umlMessage, MsgPtr ptr) {
 1768  0
         int pre = 0;
 1769  0
         int local = 0;
 1770  0
         Object/*MMessage*/ maxmsg = null;
 1771  
         Object activator;
 1772  
 
 1773  0
         if (umlMessage == null) {
 1774  0
             ptr.message = null;
 1775  0
             return 0;
 1776  
         }
 1777  
 
 1778  0
         activator = Model.getFacade().getActivator(umlMessage);
 1779  
         for (Object predecessor 
 1780  0
                 : Model.getFacade().getPredecessors(umlMessage)) {
 1781  0
             if (Model.getFacade().getActivator(predecessor) 
 1782  
                     != activator) {
 1783  0
                 continue;
 1784  
             }
 1785  0
             int p = recCountPredecessors(predecessor, null) + 1;
 1786  0
             if (p > pre) {
 1787  0
                 pre = p;
 1788  0
                 maxmsg = predecessor;
 1789  
             }
 1790  0
             local++;
 1791  0
         }
 1792  
 
 1793  0
         if (ptr != null) {
 1794  0
             ptr.message = maxmsg;
 1795  
         }
 1796  
 
 1797  0
         return Math.max(pre, local);
 1798  
     }
 1799  
     
 1800  
 }