Coverage Report - org.argouml.notation.providers.uml.NotationUtilityUml
 
Classes in this File Line Coverage Branch Coverage Complexity
NotationUtilityUml
8%
32/387
0%
2/320
7.158
NotationUtilityUml$1
11%
1/9
0%
0/8
7.158
NotationUtilityUml$10
14%
1/7
0%
0/6
7.158
NotationUtilityUml$2
16%
1/6
0%
0/4
7.158
NotationUtilityUml$3
25%
1/4
0%
0/2
7.158
NotationUtilityUml$4
14%
1/7
0%
0/6
7.158
NotationUtilityUml$5
14%
1/7
0%
0/6
7.158
NotationUtilityUml$6
11%
1/9
0%
0/6
7.158
NotationUtilityUml$7
14%
1/7
0%
0/6
7.158
NotationUtilityUml$8
14%
1/7
0%
0/6
7.158
NotationUtilityUml$9
14%
1/7
0%
0/6
7.158
NotationUtilityUml$PropertyOperation
N/A
N/A
7.158
NotationUtilityUml$PropertySpecialString
50%
4/8
0%
0/2
7.158
 
 1  
 /* $Id: NotationUtilityUml.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.ArrayList;
 43  
 import java.util.Collection;
 44  
 import java.util.Iterator;
 45  
 import java.util.List;
 46  
 import java.util.Map;
 47  
 import java.util.NoSuchElementException;
 48  
 import java.util.Stack;
 49  
 
 50  
 import org.argouml.i18n.Translator;
 51  
 import org.argouml.kernel.Project;
 52  
 import org.argouml.kernel.ProjectManager;
 53  
 import org.argouml.kernel.ProjectSettings;
 54  
 import org.argouml.model.Model;
 55  
 import org.argouml.uml.StereotypeUtility;
 56  
 import org.argouml.util.CustomSeparator;
 57  
 import org.argouml.util.MyTokenizer;
 58  
 
 59  
 /**
 60  
  * This class is a utility for the UML notation.
 61  
  *
 62  
  * @author Michiel van der Wulp
 63  
  */
 64  900
 public final class NotationUtilityUml {
 65  
     /**
 66  
      * The array of special properties for attributes.
 67  
      */
 68  
     static PropertySpecialString[] attributeSpecialStrings;
 69  
 
 70  
     /**
 71  
      * The list of CustomSeparators to use when tokenizing attributes.
 72  
      */
 73  
     static List<CustomSeparator> attributeCustomSep;
 74  
 
 75  
     /**
 76  
      * The array of special properties for operations.
 77  
      */
 78  
     static PropertySpecialString[] operationSpecialStrings;
 79  
 
 80  
     /**
 81  
      * The List of CustomSeparators to use when tokenizing attributes.
 82  
      */
 83  
     static final List<CustomSeparator> operationCustomSep;
 84  
 
 85  
     /**
 86  
      * The list of CustomSeparators to use when tokenizing parameters.
 87  
      */
 88  
     private static final List<CustomSeparator> parameterCustomSep;
 89  
 
 90  
     private static final String LIST_SEPARATOR = ", ";
 91  
 
 92  
     /**
 93  
      * The character with a meaning as a visibility at the start
 94  
      * of an attribute.
 95  
      */
 96  
     static final String VISIBILITYCHARS = "+#-~";
 97  
 
 98  
     /**
 99  
      * The constructor.
 100  
      */
 101  0
     public NotationUtilityUml() { }
 102  
 
 103  
     /* TODO: Can we put the static block within the init()? */
 104  
     static {
 105  900
         attributeSpecialStrings = new PropertySpecialString[2];
 106  
 
 107  900
         attributeCustomSep = new ArrayList<CustomSeparator>();
 108  900
         attributeCustomSep.add(MyTokenizer.SINGLE_QUOTED_SEPARATOR);
 109  900
         attributeCustomSep.add(MyTokenizer.DOUBLE_QUOTED_SEPARATOR);
 110  900
         attributeCustomSep.add(MyTokenizer.PAREN_EXPR_STRING_SEPARATOR);
 111  
 
 112  900
         operationSpecialStrings = new PropertySpecialString[8];
 113  
 
 114  900
         operationCustomSep = new ArrayList<CustomSeparator>();
 115  900
         operationCustomSep.add(MyTokenizer.SINGLE_QUOTED_SEPARATOR);
 116  900
         operationCustomSep.add(MyTokenizer.DOUBLE_QUOTED_SEPARATOR);
 117  900
         operationCustomSep.add(MyTokenizer.PAREN_EXPR_STRING_SEPARATOR);
 118  
 
 119  900
         parameterCustomSep = new ArrayList<CustomSeparator>();
 120  900
         parameterCustomSep.add(MyTokenizer.SINGLE_QUOTED_SEPARATOR);
 121  900
         parameterCustomSep.add(MyTokenizer.DOUBLE_QUOTED_SEPARATOR);
 122  900
         parameterCustomSep.add(MyTokenizer.PAREN_EXPR_STRING_SEPARATOR);
 123  900
     }
 124  
 
 125  
     static void init() {
 126  900
         int assPos = 0;
 127  900
         attributeSpecialStrings[assPos++] =
 128  
             new PropertySpecialString("frozen",
 129  900
                 new PropertyOperation() {
 130  
                     public void found(Object element, String value) {
 131  0
                         if (Model.getFacade().isAStructuralFeature(element)) {
 132  0
                             if (value == null) { 
 133  
                                 /* the text was: {frozen} */
 134  0
                                 Model.getCoreHelper().setReadOnly(element, true);
 135  0
                             } else if ("false".equalsIgnoreCase(value)) {
 136  
                                 /* the text was: {frozen = false} */
 137  0
                                 Model.getCoreHelper().setReadOnly(element, false);
 138  0
                             } else if ("true".equalsIgnoreCase(value)) {
 139  
                                 /* the text was: {frozen = true} */
 140  0
                                 Model.getCoreHelper().setReadOnly(element, true);
 141  
                             }
 142  
                         }
 143  0
                     }
 144  
                 });
 145  
         
 146  
         // TODO: AddOnly has been removed in UML 2.x, so we should phase out
 147  
         // support of it - tfm - 20070529
 148  900
         attributeSpecialStrings[assPos++] =
 149  
             new PropertySpecialString("addonly",
 150  900
                 new PropertyOperation() {
 151  
                     public void found(Object element, String value) {
 152  0
                         if (Model.getFacade().isAStructuralFeature(element)) {
 153  0
                             if ("false".equalsIgnoreCase(value)) {
 154  0
                                 Model.getCoreHelper().setReadOnly(element, true);
 155  
                             } else {
 156  0
                                 Model.getCoreHelper().setChangeability(element,
 157  
                                     Model.getChangeableKind().getAddOnly());
 158  
                             }
 159  
                         }
 160  0
                     }
 161  
                 });
 162  
 
 163  900
         assert assPos == attributeSpecialStrings.length;
 164  
 
 165  900
         operationSpecialStrings = new PropertySpecialString[8];
 166  900
         int ossPos = 0;
 167  900
         operationSpecialStrings[ossPos++] =
 168  
             new PropertySpecialString("sequential",
 169  900
                 new PropertyOperation() {
 170  
                     public void found(Object element, String value) {
 171  0
                         if (Model.getFacade().isAOperation(element)) {
 172  0
                             Model.getCoreHelper().setConcurrency(element,
 173  
                                 Model.getConcurrencyKind().getSequential());
 174  
                         }
 175  0
                     }
 176  
                 });
 177  900
         operationSpecialStrings[ossPos++] =
 178  
             new PropertySpecialString("guarded",
 179  900
                 new PropertyOperation() {
 180  
                     public void found(Object element, String value) {
 181  0
                         Object kind = Model.getConcurrencyKind().getGuarded();
 182  0
                         if (value != null && value.equalsIgnoreCase("false")) {
 183  0
                             kind = Model.getConcurrencyKind().getSequential();
 184  
                         }
 185  0
                         if (Model.getFacade().isAOperation(element)) {
 186  0
                             Model.getCoreHelper().setConcurrency(element, kind);
 187  
                         }
 188  0
                     }
 189  
                 });
 190  900
         operationSpecialStrings[ossPos++] =
 191  
             new PropertySpecialString("concurrent",
 192  900
                 new PropertyOperation() {
 193  
                     public void found(Object element, String value) {
 194  0
                         Object kind =
 195  
                             Model.getConcurrencyKind().getConcurrent();
 196  0
                         if (value != null && value.equalsIgnoreCase("false")) {
 197  0
                             kind = Model.getConcurrencyKind().getSequential();
 198  
                         }
 199  0
                         if (Model.getFacade().isAOperation(element)) {
 200  0
                             Model.getCoreHelper().setConcurrency(element, kind);
 201  
                         }
 202  0
                     }
 203  
                 });
 204  900
         operationSpecialStrings[ossPos++] =
 205  
             new PropertySpecialString("concurrency",
 206  900
                 new PropertyOperation() {
 207  
                     public void found(Object element, String value) {
 208  0
                         Object kind =
 209  
                             Model.getConcurrencyKind().getSequential();
 210  0
                         if ("guarded".equalsIgnoreCase(value)) {
 211  0
                             kind = Model.getConcurrencyKind().getGuarded();
 212  0
                         } else if ("concurrent".equalsIgnoreCase(value)) {
 213  0
                             kind = Model.getConcurrencyKind().getConcurrent();
 214  
                         }
 215  0
                         if (Model.getFacade().isAOperation(element)) {
 216  0
                             Model.getCoreHelper().setConcurrency(element, kind);
 217  
                         }
 218  0
                     }
 219  
                 });
 220  900
         operationSpecialStrings[ossPos++] =
 221  
             new PropertySpecialString("abstract",
 222  900
                 new PropertyOperation() {
 223  
                     public void found(Object element, String value) {
 224  0
                         boolean isAbstract = true;
 225  0
                         if (value != null && value.equalsIgnoreCase("false")) {
 226  0
                             isAbstract = false;
 227  
                         }
 228  0
                         if (Model.getFacade().isAOperation(element)) {
 229  0
                             Model.getCoreHelper().setAbstract(
 230  
                                     element,
 231  
                                     isAbstract);
 232  
                         }
 233  0
                     }
 234  
                 });
 235  900
         operationSpecialStrings[ossPos++] =
 236  
             new PropertySpecialString("leaf",
 237  900
                 new PropertyOperation() {
 238  
                     public void found(Object element, String value) {
 239  0
                         boolean isLeaf = true;
 240  0
                         if (value != null && value.equalsIgnoreCase("false")) {
 241  0
                             isLeaf = false;
 242  
                         }
 243  0
                         if (Model.getFacade().isAOperation(element)) {
 244  0
                             Model.getCoreHelper().setLeaf(element, isLeaf);
 245  
                         }
 246  0
                     }
 247  
                 });
 248  900
         operationSpecialStrings[ossPos++] =
 249  
             new PropertySpecialString("query",
 250  900
                 new PropertyOperation() {
 251  
                     public void found(Object element, String value) {
 252  0
                         boolean isQuery = true;
 253  0
                         if (value != null && value.equalsIgnoreCase("false")) {
 254  0
                             isQuery = false;
 255  
                         }
 256  0
                         if (Model.getFacade().isABehavioralFeature(element)) {
 257  0
                             Model.getCoreHelper().setQuery(element, isQuery);
 258  
                         }
 259  0
                     }
 260  
                 });
 261  900
         operationSpecialStrings[ossPos++] =
 262  
             new PropertySpecialString("root",
 263  900
                 new PropertyOperation() {
 264  
                     public void found(Object element, String value) {
 265  0
                         boolean isRoot = true;
 266  0
                         if (value != null && value.equalsIgnoreCase("false")) {
 267  0
                             isRoot = false;
 268  
                         }
 269  0
                         if (Model.getFacade().isAOperation(element)) {
 270  0
                             Model.getCoreHelper().setRoot(element, isRoot);
 271  
                         }
 272  0
                     }
 273  
                 });
 274  
 
 275  900
         assert ossPos == operationSpecialStrings.length;
 276  900
     }
 277  
 
 278  
     /**
 279  
      * Parse a string on the format:
 280  
      * <pre>
 281  
      *     [ &lt;&lt; stereotype &gt;&gt;] [+|-|#|~] [full_pathname ::] [name]
 282  
      * </pre>
 283  
      * 
 284  
      * @param me   The ModelElement <em>text</em> describes.
 285  
      * @param text A String on the above format.
 286  
      * @throws ParseException
 287  
      *             when it detects an error in the attribute string. See also
 288  
      *             ParseError.getErrorOffset().
 289  
      */
 290  
     protected static void parseModelElement(Object me, String text)
 291  
         throws ParseException {
 292  
         MyTokenizer st;
 293  
 
 294  0
         List<String> path = null;
 295  0
         String name = null;
 296  0
         StringBuilder stereotype = null;
 297  
         String token;
 298  
 
 299  
         try {
 300  0
             st = new MyTokenizer(text, "<<,\u00AB,\u00BB,>>,::");
 301  0
             while (st.hasMoreTokens()) {
 302  0
                 token = st.nextToken();
 303  
 
 304  0
                 if ("<<".equals(token) || "\u00AB".equals(token)) {
 305  0
                     if (stereotype != null) {
 306  0
                         String msg = 
 307  
                             "parsing.error.model-element-name.twin-stereotypes";
 308  0
                         throw new ParseException(Translator.localize(msg),
 309  
                                 st.getTokenIndex());
 310  
                     }
 311  
 
 312  0
                     stereotype = new StringBuilder();
 313  
                     while (true) {
 314  0
                         token = st.nextToken();
 315  0
                         if (">>".equals(token) || "\u00BB".equals(token)) {
 316  0
                             break;
 317  
                         }
 318  0
                         stereotype.append(token);
 319  
                     }
 320  0
                 } else if ("::".equals(token)) {
 321  0
                     if (name != null) {
 322  0
                         name = name.trim();
 323  
                     }
 324  
 
 325  0
                     if (path != null && (name == null || "".equals(name))) {
 326  0
                         String msg = 
 327  
                             "parsing.error.model-element-name.anon-qualifiers";
 328  0
                         throw new ParseException(Translator.localize(msg), 
 329  
                                 st.getTokenIndex());
 330  
                     }
 331  
 
 332  0
                     if (path == null) {
 333  0
                         path = new ArrayList<String>();
 334  
                     }
 335  0
                     if (name != null) {
 336  0
                         path.add(name);
 337  
                     }
 338  0
                     name = null;
 339  
                 } else {
 340  0
                     if (name != null) {
 341  0
                         String msg = 
 342  
                             "parsing.error.model-element-name.twin-names";
 343  0
                         throw new ParseException(Translator.localize(msg), 
 344  
                                 st.getTokenIndex());
 345  
                     }
 346  
 
 347  0
                     name = token;
 348  
                 }
 349  
             }
 350  0
         } catch (NoSuchElementException nsee) {
 351  0
             String msg = 
 352  
                 "parsing.error.model-element-name.unexpected-name-element";
 353  0
             throw new ParseException(Translator.localize(msg),
 354  
                     text.length());
 355  0
         } catch (ParseException pre) {
 356  0
             throw pre;
 357  0
         }
 358  
 
 359  0
         if (name != null) {
 360  0
             name = name.trim();
 361  
         }
 362  
 
 363  0
         if (path != null && (name == null || "".equals(name))) {
 364  0
             String msg = "parsing.error.model-element-name.must-end-with-name";
 365  0
             throw new ParseException(Translator.localize(msg), 0);
 366  
         }
 367  
 
 368  0
         if (name != null && name.startsWith("+")) {
 369  0
             name = name.substring(1).trim();
 370  0
             Model.getCoreHelper().setVisibility(me,
 371  
                             Model.getVisibilityKind().getPublic());
 372  
         }
 373  0
         if (name != null && name.startsWith("-")) {
 374  0
             name = name.substring(1).trim();
 375  0
             Model.getCoreHelper().setVisibility(me,
 376  
                             Model.getVisibilityKind().getPrivate());
 377  
         }
 378  0
         if (name != null && name.startsWith("#")) {
 379  0
             name = name.substring(1).trim();
 380  0
             Model.getCoreHelper().setVisibility(me,
 381  
                             Model.getVisibilityKind().getProtected());
 382  
         }
 383  0
         if (name != null && name.startsWith("~")) {
 384  0
             name = name.substring(1).trim();
 385  0
             Model.getCoreHelper().setVisibility(me,
 386  
                             Model.getVisibilityKind().getPackage());
 387  
         }
 388  0
         if (name != null) {
 389  0
             Model.getCoreHelper().setName(me, name);
 390  
         }
 391  
 
 392  0
         StereotypeUtility.dealWithStereotypes(me, stereotype, false);
 393  
 
 394  0
         if (path != null) {
 395  0
             Object nspe =
 396  
                 Model.getModelManagementHelper().getElement(
 397  
                         path,
 398  
                         Model.getFacade().getRoot(me));
 399  
 
 400  0
             if (nspe == null || !(Model.getFacade().isANamespace(nspe))) {
 401  0
                 String msg = 
 402  
                         "parsing.error.model-element-name.namespace-unresolved";
 403  0
                 throw new ParseException(Translator.localize(msg), 
 404  
                         0);
 405  
             }
 406  0
             if (!Model.getCoreHelper().isValidNamespace(me, nspe)) {
 407  0
                 String msg = 
 408  
                         "parsing.error.model-element-name.namespace-invalid";
 409  0
                 throw new ParseException(Translator.localize(msg), 
 410  
                         0);
 411  
             }
 412  
 
 413  0
             Model.getCoreHelper().addOwnedElement(nspe, me);
 414  
         }
 415  0
     }
 416  
     
 417  
     /**
 418  
      * Utility function to determine the presence of a key. 
 419  
      * The default is false.
 420  
      * 
 421  
      * @param key the string for the key
 422  
      * @param map the Map to check for the presence 
 423  
      * and value of the key
 424  
      * @return true if the value for the key is true, otherwise false
 425  
      */
 426  
     public static boolean isValue(final String key, final Map map) {
 427  0
         if (map == null) {
 428  0
             return false;
 429  
         }
 430  0
         Object o = map.get(key);
 431  0
         if (!(o instanceof Boolean)) {
 432  0
             return false;
 433  
         }
 434  0
         return ((Boolean) o).booleanValue();
 435  
     }
 436  
     
 437  
     /**
 438  
      * Returns a visibility String either for a VisibilityKind or a model
 439  
      * element.
 440  
      * 
 441  
      * @param o a modelelement or a visibilitykind
 442  
      * @return a string. May be the empty string, but guaranteed not to be null
 443  
      */
 444  
     public static String generateVisibility2(Object o) {
 445  0
         if (o == null) {
 446  0
             return "";
 447  
         }
 448  0
         if (Model.getFacade().isANamedElement(o)) {
 449  0
             if (Model.getFacade().isPublic(o)) {
 450  0
                 return "+";
 451  
             }
 452  0
             if (Model.getFacade().isPrivate(o)) {
 453  0
                 return "-";
 454  
             }
 455  0
             if (Model.getFacade().isProtected(o)) {
 456  0
                 return "#";
 457  
             }
 458  0
             if (Model.getFacade().isPackage(o)) {
 459  0
                 return "~";
 460  
             }
 461  
         }
 462  0
         if (Model.getFacade().isAVisibilityKind(o)) {
 463  0
             if (Model.getVisibilityKind().getPublic().equals(o)) {
 464  0
                 return "+";
 465  
             }
 466  0
             if (Model.getVisibilityKind().getPrivate().equals(o)) {
 467  0
                 return "-";
 468  
             }
 469  0
             if (Model.getVisibilityKind().getProtected().equals(o)) {
 470  0
                 return "#";
 471  
             }
 472  0
             if (Model.getVisibilityKind().getPackage().equals(o)) {
 473  0
                 return "~";
 474  
             }
 475  
         }
 476  0
         return "";
 477  
     }
 478  
 
 479  
     /**
 480  
      * @param modelElement the UML element to generate for
 481  
      * @return a string which represents the path
 482  
      */
 483  
     protected static String generatePath(Object modelElement) {
 484  0
         StringBuilder s = new StringBuilder();
 485  0
         Object p = modelElement;
 486  0
         Stack<String> stack = new Stack<String>();
 487  0
         Object ns = Model.getFacade().getNamespace(p);
 488  0
         while (ns != null && !Model.getFacade().isAModel(ns)) {
 489  0
             stack.push(Model.getFacade().getName(ns));
 490  0
             ns = Model.getFacade().getNamespace(ns);
 491  
         }
 492  0
         while (!stack.isEmpty()) {
 493  0
             s.append(stack.pop() + "::");
 494  
         }
 495  
 
 496  0
         if (s.length() > 0 && !(s.lastIndexOf(":") == s.length() - 1)) {
 497  0
             s.append("::");
 498  
         }
 499  0
         return s.toString();
 500  
     }
 501  
 
 502  
     /**
 503  
      * Parses a parameter list and aligns the parameter list in op to that
 504  
      * specified in param. A parameter list generally has the following syntax:
 505  
      *
 506  
      * <pre>
 507  
      * param := [inout] [name] [: type] [= initial value]
 508  
      * list := [param] [, param]*
 509  
      * </pre>
 510  
      *
 511  
      * <code>inout</code> is optional and if omitted the old value preserved.
 512  
      * If no value has been assigned, then <code>in </code> is assumed.<p>
 513  
      *
 514  
      * <code>name</code>, <code>type</code> and <code>initial value</code>
 515  
      * are optional and if omitted the old value preserved.<p>
 516  
      *
 517  
      * <code>type</code> and <code>initial value</code> can be given
 518  
      * in any order.<p>
 519  
      *
 520  
      * Unspecified properties is carried over by position, so if a parameter is
 521  
      * inserted into the list, then it will inherit properties from the
 522  
      * parameter that was there before for unspecified properties.<p>
 523  
      *
 524  
      * This syntax is compatible with the UML 1.3 specification.
 525  
      *
 526  
      * @param op
 527  
      *            The operation the parameter list belongs to.
 528  
      * @param param
 529  
      *            The parameter list, without enclosing parentheses.
 530  
      * @param paramOffset
 531  
      *            The offset to the beginning of the parameter list. Used for
 532  
      *            error reports.
 533  
      * @throws java.text.ParseException
 534  
      *             when it detects an error in the attribute string. See also
 535  
      *             ParseError.getErrorOffset().
 536  
      */
 537  
     static void parseParamList(Object op, String param, int paramOffset)
 538  
         throws ParseException {
 539  0
         MyTokenizer st =
 540  
             new MyTokenizer(param, " ,\t,:,=,\\,", parameterCustomSep);
 541  
         // Copy returned parameters because it will be a live collection for MDR
 542  0
         Collection origParam =
 543  
             new ArrayList(Model.getFacade().getParameters(op));
 544  0
         Object ns = Model.getFacade().getRoot(op);
 545  0
         if (Model.getFacade().isAOperation(op)) {
 546  0
             Object ow = Model.getFacade().getOwner(op);
 547  
 
 548  0
             if (ow != null && Model.getFacade().getNamespace(ow) != null) {
 549  0
                 ns = Model.getFacade().getNamespace(ow);
 550  
             }
 551  
         }
 552  
 
 553  0
         Iterator it = origParam.iterator();
 554  0
         while (st.hasMoreTokens()) {
 555  0
             String kind = null;
 556  0
             String name = null;
 557  
             String tok;
 558  0
             String type = null;
 559  0
             StringBuilder value = null;
 560  0
             Object p = null;
 561  0
             boolean hasColon = false;
 562  0
             boolean hasEq = false;
 563  
 
 564  0
             while (it.hasNext() && p == null) {
 565  0
                 p = it.next();
 566  0
                 if (Model.getFacade().isReturn(p)) {
 567  0
                     p = null;
 568  
                 }
 569  
             }
 570  
 
 571  0
             while (st.hasMoreTokens()) {
 572  0
                 tok = st.nextToken();
 573  
 
 574  0
                 if (",".equals(tok)) {
 575  0
                     break;
 576  0
                 } else if (" ".equals(tok) || "\t".equals(tok)) {
 577  0
                     if (hasEq) {
 578  0
                         value.append(tok);
 579  
                     }
 580  0
                 } else if (":".equals(tok)) {
 581  0
                     hasColon = true;
 582  0
                     hasEq = false;
 583  0
                 } else if ("=".equals(tok)) {
 584  0
                     if (value != null) {
 585  0
                             String msg =
 586  
                             "parsing.error.notation-utility.two-default-values";
 587  0
                         throw new ParseException(Translator.localize(msg),
 588  
                                 paramOffset + st.getTokenIndex());
 589  
                     }
 590  0
                     hasEq = true;
 591  0
                     hasColon = false;
 592  0
                     value = new StringBuilder();
 593  0
                 } else if (hasColon) {
 594  0
                     if (type != null) {
 595  0
                         String msg = "parsing.error.notation-utility.two-types";
 596  0
                         throw new ParseException(Translator.localize(msg),
 597  
                                 paramOffset + st.getTokenIndex());
 598  
                     }
 599  
 
 600  0
                     if (tok.charAt(0) == '\'' || tok.charAt(0) == '\"') {
 601  0
                         String msg =
 602  
                             "parsing.error.notation-utility.type-quoted";
 603  0
                         throw new ParseException(Translator.localize(msg),
 604  
                                 paramOffset + st.getTokenIndex());
 605  
                     }
 606  
 
 607  0
                     if (tok.charAt(0) == '(') {
 608  0
                         String msg =
 609  
                             "parsing.error.notation-utility.type-expr";
 610  0
                         throw new ParseException(Translator.localize(msg),
 611  
                                 paramOffset + st.getTokenIndex());
 612  
                     }
 613  
 
 614  0
                     type = tok;
 615  0
                 } else if (hasEq) {
 616  0
                     value.append(tok);
 617  
                 } else {
 618  0
                     if (name != null && kind != null) {
 619  0
                         String msg =
 620  
                             "parsing.error.notation-utility.extra-text";
 621  0
                         throw new ParseException(Translator.localize(msg),
 622  
                                 paramOffset + st.getTokenIndex());
 623  
                     }
 624  
 
 625  0
                     if (tok.charAt(0) == '\'' || tok.charAt(0) == '\"') {
 626  0
                         String msg =
 627  
                             "parsing.error.notation-utility.name-kind-quoted";
 628  0
                         throw new ParseException(
 629  
                                 Translator.localize(msg),
 630  
                                 paramOffset + st.getTokenIndex());
 631  
                     }
 632  
 
 633  0
                     if (tok.charAt(0) == '(') {
 634  0
                         String msg =
 635  
                             "parsing.error.notation-utility.name-kind-expr";
 636  0
                         throw new ParseException(
 637  
                                 Translator.localize(msg),
 638  
                                 paramOffset + st.getTokenIndex());
 639  
                     }
 640  
 
 641  0
                     kind = name;
 642  0
                     name = tok;
 643  
                 }
 644  
             }
 645  
 
 646  0
             if (p == null) {
 647  
                 /* Leave the type undefined (see issue 6145): */
 648  0
                 p = Model.getCoreFactory().buildParameter(op, null);
 649  
             }
 650  
 
 651  0
             if (name != null) {
 652  0
                 Model.getCoreHelper().setName(p, name.trim());
 653  
             }
 654  
 
 655  0
             if (kind != null) {
 656  0
                 setParamKind(p, kind.trim());
 657  
             }
 658  
 
 659  0
             if (type != null) {
 660  0
                 Model.getCoreHelper().setType(p, getType(type.trim(), ns));
 661  
             }
 662  
 
 663  0
             if (value != null) {
 664  
                 // TODO: Find a better default language
 665  
                 // TODO: We should know the notation language, since it is us
 666  0
                 Project project =
 667  
                     ProjectManager.getManager().getCurrentProject();
 668  0
                 ProjectSettings ps = project.getProjectSettings();
 669  0
                 String notationLanguage = ps.getNotationLanguage();
 670  
 
 671  0
                 Object initExpr =
 672  
                     Model.getDataTypesFactory()
 673  
                         .createExpression(
 674  
                                 notationLanguage,
 675  
                                 value.toString().trim());
 676  0
                 Model.getCoreHelper().setDefaultValue(p, initExpr);
 677  
             }
 678  0
         }
 679  
 
 680  0
         while (it.hasNext()) {
 681  0
             Object p = it.next();
 682  0
             if (!Model.getFacade().isReturn(p)) {
 683  0
                 Model.getCoreHelper().removeParameter(op, p);
 684  
             }
 685  0
         }
 686  0
     }
 687  
 
 688  
     /**
 689  
      * Set a parameters kind according to a string description of
 690  
      * that kind.
 691  
      * @param parameter the parameter
 692  
      * @param description the string description
 693  
      */
 694  
     private static void setParamKind(Object parameter, String description) {
 695  
         Object kind;
 696  0
         if ("out".equalsIgnoreCase(description)) {
 697  0
             kind = Model.getDirectionKind().getOutParameter();
 698  0
         } else if ("inout".equalsIgnoreCase(description)) {
 699  0
             kind = Model.getDirectionKind().getInOutParameter();
 700  
         } else {
 701  0
             kind = Model.getDirectionKind().getInParameter();
 702  
         }
 703  0
         Model.getCoreHelper().setKind(parameter, kind);
 704  0
     }
 705  
 
 706  
     /**
 707  
      * Finds the classifier associated with the type named in name.
 708  
      *
 709  
      * @param name
 710  
      *            The name of the type to get.
 711  
      * @param defaultSpace
 712  
      *            The default name-space to place the type in.
 713  
      * @return The classifier associated with the name.
 714  
      */
 715  
     static Object getType(String name, Object defaultSpace) {
 716  0
         Object type = null;
 717  0
         Project p = ProjectManager.getManager().getCurrentProject();
 718  
         // Should we be getting this from the GUI? BT 11 aug 2002
 719  0
         type = p.findType(name, false);
 720  0
         if (type == null) { // no type defined yet
 721  0
             type = Model.getCoreFactory().buildClass(name,
 722  
                     defaultSpace);
 723  
         }
 724  0
         return type;
 725  
     }
 726  
 
 727  
     /**
 728  
      * Applies a List of name/value pairs of properties to a model element.
 729  
      * The name is treated as the tag of a tagged value unless it is one of the
 730  
      * PropertySpecialStrings, in which case the action of the
 731  
      * PropertySpecialString is invoked.
 732  
      *
 733  
      * @param elem
 734  
      *            An model element to apply the properties to.
 735  
      * @param prop
 736  
      *            A List with name, value pairs of properties.
 737  
      * @param spec
 738  
      *            An array of PropertySpecialStrings to use.
 739  
      */
 740  
     static void setProperties(Object elem, List<String> prop,
 741  
             PropertySpecialString[] spec) {
 742  
         String name;
 743  
         String value;
 744  
         int i, j;
 745  
 
 746  
     nextProp:
 747  0
         for (i = 0; i + 1 < prop.size(); i += 2) {
 748  0
             name = prop.get(i);
 749  0
             value = prop.get(i + 1);
 750  
 
 751  0
             if (name == null) {
 752  0
                 continue;
 753  
             }
 754  
 
 755  0
             name = name.trim();
 756  0
             if (value != null) {
 757  0
                 value = value.trim();
 758  
             }
 759  
 
 760  
             /* If the current property occurs a second time
 761  
              * in the given list of properties, then skip it: */
 762  0
             for (j = i + 2; j < prop.size(); j += 2) {
 763  0
                 String s = prop.get(j);
 764  0
                 if (s != null && name.equalsIgnoreCase(s.trim())) {
 765  0
                     continue nextProp;
 766  
                 }
 767  
             }
 768  
 
 769  0
             if (spec != null) {
 770  0
                 for (j = 0; j < spec.length; j++) {
 771  0
                     if (spec[j].invoke(elem, name, value)) {
 772  0
                         continue nextProp;
 773  
                     }
 774  
                 }
 775  
             }
 776  
 
 777  0
             Model.getCoreHelper().setTaggedValue(elem, name, value);
 778  
         }
 779  0
     }
 780  
 
 781  
 
 782  
     /**
 783  
      * Interface specifying the operation to take when a
 784  
      * PropertySpecialString is matched.
 785  
      *
 786  
      * @author Michael Stockman
 787  
      * @since 0.11.2
 788  
      * @see PropertySpecialString
 789  
      */
 790  
     interface PropertyOperation {
 791  
         /**
 792  
          * Invoked by PropertySpecialString when it has matched a property name.
 793  
          *
 794  
          * @param element
 795  
          *            The element on which the property was set.
 796  
          * @param value
 797  
          *            The value of the property,
 798  
          *            may be null if no value was given.
 799  
          */
 800  
         void found(Object element, String value);
 801  
     }
 802  
 
 803  
     /**
 804  
      * Declares a string that should take special action when it is found
 805  
      * as a property in
 806  
      * {@link ParserDisplay#setProperties ParserDisplay.setProperties}.<p>
 807  
      *
 808  
      * <em>Example:</em>
 809  
      *
 810  
      * <pre>
 811  
      * attributeSpecialStrings[0] =
 812  
      *     new PropertySpecialString(&quot;frozen&quot;,
 813  
      *         new PropertyOperation() {
 814  
      *             public void found(Object element, String value) {
 815  
      *                 if (Model.getFacade().isAStructuralFeature(element))
 816  
      *                     Model.getFacade().setChangeable(element,
 817  
      *                          (value != null &amp;&amp; value
 818  
      *                             .equalsIgnoreCase(&quot;false&quot;)));
 819  
      *             }
 820  
      *         });
 821  
      * </pre>
 822  
      *
 823  
      * Taken from the (former) ParserDisplay constructor.
 824  
      * It creates a PropertySpecialString that is invoken when the String
 825  
      * "frozen" is found as a property name. Then
 826  
      * the found mehod in the anonymous inner class
 827  
      * defined on the 2nd line is invoked and performs
 828  
      * a custom action on the element on which the property was
 829  
      * specified by the user. In this case it does a setChangeability
 830  
      * on an attribute instead of setting a tagged value,
 831  
      * which would not have the desired effect.
 832  
      *
 833  
      * @author Michael Stockman
 834  
      * @since 0.11.2
 835  
      * @see PropertyOperation
 836  
      * @see ParserDisplay#setProperties
 837  
      */
 838  
     static class PropertySpecialString {
 839  
         private String name;
 840  
 
 841  
         private PropertyOperation op;
 842  
 
 843  
         /**
 844  
          * Constructs a new PropertySpecialString that will invoke the
 845  
          * action in propop when {@link #invoke(Object, String, String)} is
 846  
          * called with name equal to str and then return true from invoke.
 847  
          *
 848  
          * @param str
 849  
          *            The name of this PropertySpecialString.
 850  
          * @param propop
 851  
          *            An object containing the method to invoke on a match.
 852  
          */
 853  9000
         public PropertySpecialString(String str, PropertyOperation propop) {
 854  9000
             name = str;
 855  9000
             op = propop;
 856  9000
         }
 857  
 
 858  
         /**
 859  
          * Called by {@link NotationUtilityUml#setProperties(Object, 
 860  
          * java.util.Vector, PropertySpecialString[])} while 
 861  
          * searching for an action to
 862  
          * invoke for a property. If it returns true, then setProperties
 863  
          * may assume that all required actions have been taken and stop
 864  
          * searching.
 865  
          *
 866  
          * @param pname
 867  
          *            The name of a property.
 868  
          * @param value
 869  
          *            The value of a property.
 870  
          * @param element
 871  
          *            A model element to apply the properties to.
 872  
          * @return <code>true</code> if an action is performed, otherwise
 873  
          *         <code>false</code>.
 874  
          */
 875  
         boolean invoke(Object element, String pname, String value) {
 876  0
             if (!name.equalsIgnoreCase(pname)) {
 877  0
                 return false;
 878  
             }
 879  0
             op.found(element, value);
 880  0
             return true;
 881  
         }
 882  
     }
 883  
 
 884  
     /**
 885  
      * Checks for ';' in Strings or chars in ';' separated tokens in order to
 886  
      * return an index to the next attribute or operation substring, -1
 887  
      * otherwise (a ';' inside a String or char delimiters is ignored).
 888  
      *
 889  
      * @param s The string to search.
 890  
      * @param start The position to start at.
 891  
      * @return the index to the next attribute
 892  
      */
 893  
     static int indexOfNextCheckedSemicolon(String s, int start) {
 894  0
         if (s == null || start < 0 || start >= s.length()) {
 895  0
             return -1;
 896  
         }
 897  
         int end;
 898  0
         boolean inside = false;
 899  0
         boolean backslashed = false;
 900  
         char c;
 901  0
         for (end = start; end < s.length(); end++) {
 902  0
             c = s.charAt(end);
 903  0
             if (!inside && c == ';') {
 904  0
                 return end;
 905  0
             } else if (!backslashed && (c == '\'' || c == '\"')) {
 906  0
                 inside = !inside;
 907  
             }
 908  0
             backslashed = (!backslashed && c == '\\');
 909  
         }
 910  0
         return end;
 911  
     }
 912  
 
 913  
     /**
 914  
      * Finds a visibility for the visibility specified by name. If no known
 915  
      * visibility can be deduced, private visibility is used.
 916  
      *
 917  
      * @param name
 918  
      *            The Java name of the visibility.
 919  
      * @return A visibility corresponding to name.
 920  
      */
 921  
     static Object getVisibility(String name) {
 922  0
         if ("+".equals(name) || "public".equals(name)) {
 923  0
             return Model.getVisibilityKind().getPublic();
 924  0
         } else if ("#".equals(name) || "protected".equals(name)) {
 925  0
             return Model.getVisibilityKind().getProtected();
 926  0
         } else if ("~".equals(name) || "package".equals(name)) {
 927  0
             return Model.getVisibilityKind().getPackage();
 928  
         } else {
 929  
             /* if ("-".equals(name) || "private".equals(name)) */
 930  0
             return Model.getVisibilityKind().getPrivate();
 931  
         }
 932  
     }
 933  
 
 934  
     /**
 935  
      * Generate the text for one or more stereotype(s).
 936  
      * 
 937  
      * @param st One of:
 938  
      *            <ul>
 939  
      *            <li>a stereotype UML object</li>
 940  
      *            <li>a string</li>
 941  
      *            <li>a collection of stereotypes</li>
 942  
      *            <li>a modelelement of which the stereotypes are retrieved</li>
 943  
      *            </ul>
 944  
      * @param useGuillemets true if Unicode double angle bracket quote
 945  
      *            characters should be used.
 946  
      * @return fully formatted string with list of stereotypes separated by
 947  
      *         commas and surround in brackets
 948  
      */
 949  
     public static String generateStereotype(Object st, boolean useGuillemets) {
 950  0
         if (st == null) {
 951  0
             return "";
 952  
         }
 953  
 
 954  0
         if (st instanceof String) {
 955  0
             return formatStereotype((String) st, useGuillemets);
 956  
         }
 957  0
         if (Model.getFacade().isAStereotype(st)) {
 958  0
             return formatStereotype(Model.getFacade().getName(st),
 959  
                     useGuillemets);
 960  
         }
 961  
 
 962  0
         if (Model.getFacade().isAModelElement(st)) {
 963  0
             st = Model.getFacade().getStereotypes(st);
 964  
         }
 965  
         
 966  0
         if (st instanceof Collection) {
 967  0
             String result = null;
 968  0
             boolean found = false;
 969  0
             for (Object stereotype : (Collection) st) {
 970  0
                 String name =  Model.getFacade().getName(stereotype);
 971  0
                 if (!found) {
 972  0
                     result = name;
 973  0
                     found = true;
 974  
                 } else {
 975  
                     // Allow concatenation order and separator to be localized
 976  0
                     result = Translator.localize("misc.stereo.concatenate",
 977  
                             new Object[] {result, name});
 978  
                 }
 979  0
             }
 980  0
             if (found) {
 981  0
                 return formatStereotype(result, useGuillemets);
 982  
             }
 983  
         }
 984  0
         return "";
 985  
     }
 986  
     
 987  
     /**
 988  
      * Create a string representation of a stereotype, keyword or comma separate
 989  
      * list of names. This method just wraps the string in <<angle brackets>> or
 990  
      * guillemets (double angle bracket characters) depending on the setting
 991  
      * of the flag <code>useGuillemets</code>.
 992  
      * 
 993  
      * @param name the name of the stereotype
 994  
      * @param useGuillemets true if Unicode double angle bracket quote
 995  
      *            characters should be used.
 996  
      * @return the string representation
 997  
      */
 998  
     public static String formatStereotype(String name, boolean useGuillemets) {
 999  0
         if (name == null || name.length() == 0) {
 1000  0
             return "";
 1001  
         }
 1002  
 
 1003  0
         String key = "misc.stereo.guillemets."
 1004  
                 + Boolean.toString(useGuillemets);
 1005  0
         return Translator.localize(key, new Object[] {name});
 1006  
     }
 1007  
     
 1008  
 
 1009  
     /**
 1010  
      * Generates the representation of a parameter on the display (diagram). The
 1011  
      * string to be returned will have the following syntax:
 1012  
      * <p>
 1013  
      * 
 1014  
      * kind name : type-expression = default-value
 1015  
      * 
 1016  
      * @see org.argouml.notation.NotationProvider2#generateParameter(java.lang.Object)
 1017  
      */
 1018  
     static String generateParameter(Object parameter) {
 1019  0
         StringBuffer s = new StringBuffer();
 1020  0
         s.append(generateKind(Model.getFacade().getKind(parameter)));
 1021  0
         if (s.length() > 0) {
 1022  0
             s.append(" ");
 1023  
         }
 1024  0
         s.append(Model.getFacade().getName(parameter));
 1025  0
         String classRef =
 1026  
             generateClassifierRef(Model.getFacade().getType(parameter));
 1027  0
         if (classRef.length() > 0) {
 1028  0
             s.append(" : ");
 1029  0
             s.append(classRef);
 1030  
         }
 1031  0
         String defaultValue =
 1032  
             generateExpression(Model.getFacade().getDefaultValue(parameter));
 1033  0
         if (defaultValue.length() > 0) {
 1034  0
             s.append(" = ");
 1035  0
             s.append(defaultValue);
 1036  
         }
 1037  0
         return s.toString();
 1038  
     }
 1039  
 
 1040  
     private static String generateExpression(Object expr) {
 1041  0
         if (Model.getFacade().isAExpression(expr)) {
 1042  0
             return generateUninterpreted(
 1043  
                     (String) Model.getFacade().getBody(expr));
 1044  0
         } else if (Model.getFacade().isAConstraint(expr)) {
 1045  0
             return generateExpression(Model.getFacade().getBody(expr));
 1046  
         }
 1047  0
         return "";
 1048  
     }
 1049  
 
 1050  
     private static String generateUninterpreted(String un) {
 1051  0
         if (un == null) {
 1052  0
             return "";
 1053  
         }
 1054  0
         return un;
 1055  
     }
 1056  
 
 1057  
     private static String generateClassifierRef(Object cls) {
 1058  0
         if (cls == null) {
 1059  0
             return "";
 1060  
         }
 1061  0
         return Model.getFacade().getName(cls);
 1062  
     }
 1063  
 
 1064  
     private static String generateKind(Object /*Parameter etc.*/ kind) {
 1065  0
         StringBuffer s = new StringBuffer();
 1066  
         // TODO: I18N
 1067  0
         if (kind == null /* "in" is the default */
 1068  
                 || kind == Model.getDirectionKind().getInParameter()) {
 1069  0
             s.append(/*"in"*/ ""); /* See issue 3421. */
 1070  0
         } else if (kind == Model.getDirectionKind().getInOutParameter()) {
 1071  0
             s.append("inout");
 1072  0
         } else if (kind == Model.getDirectionKind().getReturnParameter()) {
 1073  
             // return nothing
 1074  0
         } else if (kind == Model.getDirectionKind().getOutParameter()) {
 1075  0
             s.append("out");
 1076  
         }
 1077  0
         return s.toString();
 1078  
     }
 1079  
 
 1080  
     /**
 1081  
      * @param tv a tagged value
 1082  
      * @return a string that represents the tagged value
 1083  
      */
 1084  
     static String generateTaggedValue(Object tv) {
 1085  0
         if (tv == null) {
 1086  0
             return "";
 1087  
         }
 1088  0
         return Model.getFacade().getTagOfTag(tv)
 1089  
             + "="
 1090  
             + generateUninterpreted(Model.getFacade().getValueOfTag(tv));
 1091  
     }
 1092  
 
 1093  
     /**
 1094  
      * Generate the text of a multiplicity.
 1095  
      * 
 1096  
      * @param element a multiplicity or an element which has a multiplicity
 1097  
      * @param showSingularMultiplicity if false return the empty string for 1..1
 1098  
      *            multiplicities.
 1099  
      * @return a string containing the formatted multiplicity,
 1100  
      * or the empty string
 1101  
      */
 1102  
     public static String generateMultiplicity(Object element, 
 1103  
             boolean showSingularMultiplicity) {
 1104  
         Object multiplicity;
 1105  0
         if (Model.getFacade().isAMultiplicity(element)) { 
 1106  0
             multiplicity = element;
 1107  0
         } else if (Model.getFacade().isAUMLElement(element)) {
 1108  0
             multiplicity = Model.getFacade().getMultiplicity(element);
 1109  
         } else {
 1110  0
             throw new IllegalArgumentException();
 1111  
         }
 1112  
         // it can still be null if the UML element 
 1113  
         // did not have a multiplicity defined.
 1114  0
         if (multiplicity != null) {
 1115  0
             int upper = Model.getFacade().getUpper(multiplicity);
 1116  0
             int lower = Model.getFacade().getLower(multiplicity);
 1117  0
             if (lower != 1 || upper != 1 || showSingularMultiplicity) {
 1118  
                 // TODO: I18N
 1119  0
                 return Model.getFacade().toString(multiplicity);
 1120  
             }
 1121  
         }
 1122  0
         return "";
 1123  
     }
 1124  
     
 1125  
     /**
 1126  
      * @param umlAction the action
 1127  
      * @return the generated text (never null)
 1128  
      */
 1129  
     static String generateAction(Object umlAction) {
 1130  
         Collection c;
 1131  
         Iterator it;
 1132  
         String s;
 1133  
         StringBuilder p;
 1134  
         boolean first;
 1135  0
         if (umlAction == null) {
 1136  0
             return "";
 1137  
         }
 1138  
 
 1139  0
         Object script = Model.getFacade().getScript(umlAction);
 1140  
 
 1141  0
         if ((script != null) && (Model.getFacade().getBody(script) != null)) {
 1142  0
             s = Model.getFacade().getBody(script).toString();
 1143  
         } else {
 1144  0
             s = "";
 1145  
         }
 1146  
 
 1147  0
         p = new StringBuilder();
 1148  0
         c = Model.getFacade().getActualArguments(umlAction);
 1149  0
         if (c != null) {
 1150  0
             it = c.iterator();
 1151  0
             first = true;
 1152  0
             while (it.hasNext()) {
 1153  0
                 Object arg = it.next();
 1154  0
                 if (!first) {
 1155  
                     // TODO: I18N
 1156  0
                     p.append(", ");
 1157  
                 }
 1158  
 
 1159  0
                 if (Model.getFacade().getValue(arg) != null) {
 1160  0
                     p.append(generateExpression(
 1161  
                             Model.getFacade().getValue(arg)));
 1162  
                 }
 1163  0
                 first = false;
 1164  0
             }
 1165  
         }
 1166  0
         if (s.length() == 0 && p.length() == 0) {
 1167  0
             return "";
 1168  
         }
 1169  
 
 1170  
         /* If there are no arguments, then do not show the ().
 1171  
          * This solves issue 1758.
 1172  
          * Arguments are not supported anyhow in the UI yet.
 1173  
          * These brackets are easily confused with the brackets
 1174  
          * for the Operation of a CallAction.
 1175  
          */
 1176  0
         if (p.length() == 0) {
 1177  0
             return s;
 1178  
         }
 1179  
 
 1180  
         // TODO: I18N
 1181  0
         return s + " (" + p + ")";
 1182  
     }
 1183  
 
 1184  
     /**
 1185  
      * Generate a textual representation of the given Action or ActionSequence 
 1186  
      * according the UML standard notation.
 1187  
      * 
 1188  
      * @param a the UML Action or ActionSequence
 1189  
      * @return the generated textual representation 
 1190  
      * of the given action(sequence).
 1191  
      * This value is guaranteed NOT null.
 1192  
      */
 1193  
     public static String generateActionSequence(Object a) {
 1194  0
         if (Model.getFacade().isAActionSequence(a)) {
 1195  0
             StringBuffer str = new StringBuffer("");
 1196  0
             Collection actions = Model.getFacade().getActions(a);
 1197  0
             Iterator i = actions.iterator();
 1198  0
             if (i.hasNext()) {
 1199  0
                 str.append(generateAction(i.next()));
 1200  
             }
 1201  0
             while (i.hasNext()) {
 1202  0
                 str.append("; ");
 1203  0
                 str.append(generateAction(i.next()));
 1204  
             }
 1205  0
             return str.toString();
 1206  
         } else {
 1207  0
             return generateAction(a);
 1208  
         }
 1209  
     }
 1210  
     
 1211  
     static StringBuilder formatNameList(Collection modelElements) {
 1212  0
         return formatNameList(modelElements, LIST_SEPARATOR);
 1213  
     }
 1214  
 
 1215  
     static StringBuilder formatNameList(Collection modelElements, 
 1216  
             String separator) {
 1217  0
         StringBuilder result = new StringBuilder();
 1218  0
         for (Object element : modelElements) {
 1219  0
             String name = Model.getFacade().getName(element);
 1220  
             // TODO: Any special handling for null names? append will use "null"
 1221  0
             result.append(name).append(separator);
 1222  0
         }
 1223  0
         if (result.length() >= separator.length()) {
 1224  0
             result.delete(result.length() - separator.length(), 
 1225  
                     result.length());
 1226  
         }
 1227  0
         return result;
 1228  
     }
 1229  
 }