Coverage Report - org.argouml.kernel.ProjectManager
 
Classes in this File Line Coverage Branch Coverage Complexity
ProjectManager
79%
99/125
44%
15/34
1.667
ProjectManager$1
100%
12/12
50%
1/2
1.667
ProjectManager$2
100%
14/14
50%
1/2
1.667
ProjectManager$3
37%
3/8
N/A
1.667
 
 1  
 /* $Id: ProjectManager.java 18968 2011-01-13 14:39:23Z tfmorris $
 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  
  *    thn
 11  
  *****************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 1996-2008 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.kernel;
 40  
 
 41  
 import java.beans.PropertyChangeEvent;
 42  
 import java.beans.PropertyChangeListener;
 43  
 import java.util.ArrayList;
 44  
 import java.util.Collection;
 45  
 import java.util.LinkedList;
 46  
 import java.util.List;
 47  
 
 48  
 import javax.swing.Action;
 49  
 import javax.swing.event.EventListenerList;
 50  
 
 51  
 import org.apache.log4j.Logger;
 52  
 import org.argouml.application.events.ArgoEventPump;
 53  
 import org.argouml.application.events.ArgoEventTypes;
 54  
 import org.argouml.application.events.ArgoStatusEvent;
 55  
 import org.argouml.cognitive.Designer;
 56  
 import org.argouml.i18n.Translator;
 57  
 import org.argouml.model.Model;
 58  
 import org.argouml.model.ModelCommand;
 59  
 import org.argouml.model.ModelCommandCreationObserver;
 60  
 import org.argouml.profile.Profile;
 61  
 import org.argouml.profile.ProfileException;
 62  
 import org.argouml.uml.cognitive.ProjectMemberTodoList;
 63  
 import org.argouml.uml.diagram.ArgoDiagram;
 64  
 import org.argouml.uml.diagram.DiagramFactory;
 65  
 
 66  
 /**
 67  
  * This class manages the projects loaded in argouml,
 68  
  * and what the current project is. <p>
 69  
  *
 70  
  * Classes in ArgoUML can ask this class for the current
 71  
  * project and set the current project.  Since we only have one
 72  
  * project in ArgoUML at the moment, this class does not manage a list
 73  
  * of projects like one would expect. This could be a nice extension
 74  
  * for the future of ArgoUML.  As soon as the current project is
 75  
  * changed, a property changed event is fired. <p>
 76  
  * 
 77  
  * TODO: Move everything related to the creation of a project 
 78  
  * into the ProjectFactory.
 79  
  *
 80  
  * @since Nov 17, 2002
 81  
  * @author jaap.branderhorst@xs4all.nl
 82  
  * @stereotype singleton
 83  
  */
 84  4854
 public final class ProjectManager implements ModelCommandCreationObserver {
 85  
 
 86  
     /**
 87  
      * The name of the property that defines the current project.  The values
 88  
      * passed are Projects, not Strings.  The 'name' here refers to the name
 89  
      * of this property, not the name of the project.
 90  
      * 
 91  
      * @deprecated for 0.27.2 by tfmorris. Listeners of this event which expect
 92  
      *             it to indicate a new project being opened should listen for
 93  
      *             {@link #OPEN_PROJECTS_PROPERTY}. Listeners who think 
 94  
      *             they need to know a single global current project need
 95  
      *             to be changed to deal with things on a per-project basis.
 96  
      */
 97  
     @Deprecated
 98  
     public static final String CURRENT_PROJECT_PROPERTY_NAME = "currentProject";
 99  
 
 100  
     /**
 101  
      * Property name for list of current open projects.  Old and new property
 102  
      * values are of type Project[] (arrays of Projects).
 103  
      */
 104  
     public static final String OPEN_PROJECTS_PROPERTY = "openProjects";
 105  
 
 106  900
     private static final Logger LOG = Logger.getLogger(ProjectManager.class);
 107  
 
 108  
     /**
 109  
      * The singleton instance of this class.
 110  
      */
 111  900
     private static ProjectManager instance = new ProjectManager();
 112  
 
 113  
     /**
 114  
      * The project that is visible in the projectbrowser.
 115  
      */
 116  
     private static Project currentProject;
 117  
     
 118  900
     private static LinkedList<Project> openProjects = new LinkedList<Project>();
 119  
 
 120  
     /**
 121  
      * Flag to indicate we are creating a new current project.
 122  
      * TODO: This isn't a thread-safe way of doing mutual exclusion.
 123  
      */
 124  
     private boolean creatingCurrentProject;
 125  
 
 126  
     private Action saveAction;
 127  
     
 128  
     /**
 129  
      * The listener list.
 130  
      */
 131  900
     private EventListenerList listenerList = new EventListenerList();
 132  
 
 133  
     /**
 134  
      * The event to fire.
 135  
      *
 136  
      * TODO: Investigate! Is the purpose really to let the next call to
 137  
      * {@link #firePropertyChanged(String, Object, Object)} fire the old
 138  
      * event again if the previous invocation resulted in an exception?
 139  
      * If so, please document why. If not, fix it.
 140  
      */
 141  
     private PropertyChangeEvent event;
 142  
 
 143  
     /**
 144  
      * The singleton accessor method of this class.
 145  
      *
 146  
      * @return The singleton.
 147  
      */
 148  
     public static ProjectManager getManager() {
 149  43211
         return instance;
 150  
     }
 151  
 
 152  
     /**
 153  
      * Constructor for ProjectManager.
 154  
      */
 155  
     private ProjectManager() {
 156  900
         super();
 157  900
         Model.setModelCommandCreationObserver(this);
 158  900
     }
 159  
 
 160  
     /**
 161  
      * Adds a listener to the listener list.
 162  
      *
 163  
      * @param listener The listener to add.
 164  
      */
 165  
     public void addPropertyChangeListener(PropertyChangeListener listener) {
 166  3600
         listenerList.add(PropertyChangeListener.class, listener);
 167  3600
     }
 168  
 
 169  
     /**
 170  
      * Removes a listener from the listener list.
 171  
      *
 172  
      * @param listener The listener to remove.
 173  
      */
 174  
     public void removePropertyChangeListener(PropertyChangeListener listener) {
 175  0
         listenerList.remove(PropertyChangeListener.class, listener);
 176  0
     }
 177  
 
 178  
     /**
 179  
      * Fire an event to all members of the listener list.
 180  
      *
 181  
      * @param propertyName The name of the event.
 182  
      * @param oldValue The old value.
 183  
      * @param newValue The new value.
 184  
      */
 185  
     void firePropertyChanged(String propertyName,
 186  
                                      Object oldValue, Object newValue) {
 187  
         // Guaranteed to return a non-null array
 188  3774
         Object[] listeners = listenerList.getListenerList();
 189  
         // Process the listeners last to first, notifying
 190  
         // those that are interested in this event
 191  18870
         for (int i = listeners.length - 2; i >= 0; i -= 2) {
 192  15096
             if (listeners[i] == PropertyChangeListener.class) {
 193  
                 // Lazily create the event:
 194  15096
                 if (event == null) {
 195  3774
                     event =
 196  
                         new PropertyChangeEvent(
 197  
                             this,
 198  
                             propertyName,
 199  
                             oldValue,
 200  
                             newValue);
 201  
                 }
 202  15096
                 ((PropertyChangeListener) listeners[i + 1]).propertyChange(
 203  
                     event);
 204  
             }
 205  
         }
 206  3774
         event = null;
 207  3774
     }
 208  
 
 209  
     /**
 210  
      * Sets the current project (the project that is viewable in the
 211  
      * projectbrowser). Sets the current diagram for the project (if one
 212  
      * exists). This method fires a propertychanged event.
 213  
      * <p>
 214  
      * If the argument is null, then the current project will be forgotten
 215  
      * about.
 216  
      * 
 217  
      * @param newProject The new project.
 218  
      * @deprecated for 0.27.2 by tfmorris. There is no longer the concept of a
 219  
      *             single global "current" project. In the future, multiple
 220  
      *             projects will be able to be open at a time, so all code
 221  
      *             should be prepared to deal with multiple projects and should
 222  
      *             require a Project to be passed as an argument if they need
 223  
      *             access.
 224  
      */
 225  
     public void setCurrentProject(Project newProject) {
 226  1868
         Project oldProject = currentProject;
 227  1868
         currentProject = newProject;
 228  1868
         addProject(newProject);
 229  1868
         if (currentProject != null
 230  
             && currentProject.getActiveDiagram() == null) {
 231  0
             List<ArgoDiagram> diagrams = currentProject.getDiagramList();
 232  0
             if (diagrams != null && !diagrams.isEmpty()) {
 233  0
                 ArgoDiagram activeDiagram = diagrams.get(0);
 234  0
                 currentProject.setActiveDiagram(activeDiagram);
 235  
             }
 236  
         }
 237  1868
         notifyProjectAdded(newProject, oldProject);
 238  1868
     }
 239  
 
 240  
     private void notifyProjectAdded(Project newProject, Project oldProject) {
 241  1868
         firePropertyChanged(CURRENT_PROJECT_PROPERTY_NAME,
 242  
                 oldProject, newProject);
 243  
         // TODO: Tentative implementation. Do we want something that updates
 244  
         // the list of open projects or just simple open and close events? -tfm
 245  1868
         firePropertyChanged(OPEN_PROJECTS_PROPERTY,
 246  
                 new Project[] {oldProject}, new Project[] {newProject});
 247  1868
     }
 248  
 
 249  
     /**
 250  
      * Returns the current project (ie the project which must recently had the
 251  
      * user focus) or null if there is no current project.
 252  
      * <p>
 253  
      * This should only be used by callers who need to know the global state.
 254  
      * Most things which need a project want the project that contains them,
 255  
      * which they can discover by traversing their containing elements (e.g.
 256  
      * Fig->Diagram->DiagramSettings).
 257  
      * <p>
 258  
      * 
 259  
      * @return Project the current project or null if none
 260  
      * @deprecated for 0.27.2 by tfmorris. There is no longer the concept of a
 261  
      *             single global "current" project. In the future, multiple
 262  
      *             projects will be able to be open at a time, so all code
 263  
      *             should be prepared to deal with multiple projects and should
 264  
      *             require a Project to be passed as an argument if they need
 265  
      *             access. To get a list of all currently open projects, use
 266  
      *             {@link #getOpenProjects()}. For settings which affect
 267  
      *             renderings in diagrams use
 268  
      *             {@link org.argouml.uml.diagram.ui.ArgoFig#getSettings()}.
 269  
      */
 270  
     @Deprecated
 271  
     public Project getCurrentProject() {
 272  35948
         return currentProject;
 273  
     }
 274  
     
 275  
     /**
 276  
      * @return a list of the currently open Projects in the order they were
 277  
      * opened
 278  
      */
 279  
     public List<Project> getOpenProjects() {
 280  0
         List<Project> result = new ArrayList<Project>();
 281  0
         if (currentProject != null) {
 282  0
             result.add(currentProject);
 283  
         }
 284  0
         return result;        
 285  
     }
 286  
     
 287  
     /**
 288  
      * Makes an empty project.
 289  
      * @return Project the empty project
 290  
      */
 291  
     public Project makeEmptyProject() {
 292  54
         return makeEmptyProject(true);
 293  
     }
 294  
 
 295  
     /**
 296  
      * Make a new empty project optionally including default diagrams.
 297  
      * <p>
 298  
      * Historically new projects have been created with two default diagrams
 299  
      * (Class and Use Case). NOTE: ArgoUML currently requires at least one
 300  
      * diagram for proper operation.
 301  
      * 
 302  
      * @param addDefaultDiagrams
 303  
      *            if true the project will be be created with the two standard
 304  
      *            default diagrams (Class and Use Case)
 305  
      * @return Project the newly created project
 306  
      */
 307  
     public Project makeEmptyProject(final boolean addDefaultDiagrams) {    
 308  954
         final Command cmd = new NonUndoableCommand() {
 309  
 
 310  
             @Override
 311  
             public Object execute() {
 312  954
                 Model.getPump().stopPumpingEvents();
 313  
                 
 314  954
                 creatingCurrentProject = true;
 315  954
                 LOG.info("making empty project");
 316  954
                 Project newProject = new ProjectImpl();
 317  954
                 createDefaultModel(newProject);
 318  954
                 if (addDefaultDiagrams) {
 319  954
                     createDefaultDiagrams(newProject);
 320  
                 }
 321  954
                 creatingCurrentProject = false;
 322  954
                 setCurrentProject(newProject);
 323  954
                 Model.getPump().startPumpingEvents();
 324  954
                 return null;
 325  
             }
 326  
         };
 327  954
         cmd.execute();
 328  954
         currentProject.getUndoManager().addCommand(cmd);
 329  954
         setSaveEnabled(false);
 330  954
         return currentProject;
 331  
     }
 332  
 
 333  
     /**
 334  
      * Makes an empty profile project.
 335  
      * @return Project the empty profile project
 336  
      */
 337  
     public Project makeEmptyProfileProject() {
 338  14
         return makeEmptyProfileProject(true);
 339  
     }
 340  
 
 341  
     /**
 342  
      * Make a new empty profile project optionally including default diagrams.
 343  
      * <p>
 344  
      * Historically new projects have been created with two default diagrams
 345  
      * (Class and Use Case). NOTE: ArgoUML currently requires at least one
 346  
      * diagram for proper operation.
 347  
      * 
 348  
      * @param addDefaultDiagrams
 349  
      *            if true the project will be be created with the standard
 350  
      *            default diagram (Class)
 351  
      * @return Project the newly created profile project
 352  
      */
 353  
     public Project makeEmptyProfileProject(final boolean addDefaultDiagrams) {    
 354  14
         final Command cmd = new NonUndoableCommand() {
 355  
 
 356  
             @Override
 357  
             public Object execute() {
 358  14
                 Model.getPump().stopPumpingEvents();
 359  
                 
 360  14
                 creatingCurrentProject = true;
 361  14
                 LOG.info("making empty profile project");
 362  14
                 Project newProject = new ProjectImpl(Project.PROFILE_PROJECT);
 363  14
                 createDefaultProfile(newProject);
 364  14
                 if (addDefaultDiagrams) {
 365  14
                     ArgoDiagram d = createClassDiagram(newProject);
 366  14
                     createTodoList(newProject);
 367  14
                     newProject.setActiveDiagram(d);
 368  
                 }
 369  14
                 creatingCurrentProject = false;
 370  14
                 setCurrentProject(newProject);
 371  14
                 Model.getPump().startPumpingEvents();
 372  14
                 return null;
 373  
             }
 374  
         };
 375  14
         cmd.execute();
 376  14
         currentProject.getUndoManager().addCommand(cmd);
 377  14
         setSaveEnabled(false);
 378  14
         return currentProject;
 379  
     }
 380  
 
 381  
     /**
 382  
      * Apply all profiles from the profile configuration to a model (can be a
 383  
      * profile too).
 384  
      * 
 385  
      * @param project The project with the profile configuration.
 386  
      * @param model The model to apply the profiles to.
 387  
      */
 388  
     private void applyProfileConfiguration(Project project, Object model) {
 389  968
         Collection<Profile> c =
 390  
             project.getProfileConfiguration().getProfiles();
 391  968
         if (c != null) {
 392  968
             for (Profile p : c) {
 393  
                 try {
 394  2862
                     for (Object profile : p.getProfilePackages()) {
 395  954
                         Model.getExtensionMechanismsHelper()
 396  
                             .applyProfile(model, profile);
 397  
                     }
 398  0
                 } catch (ProfileException pe) {
 399  0
                     LOG.warn("Failed to get profile packages from profile "
 400  
                             + p.getDisplayName());
 401  5724
                 }
 402  
             }
 403  
         }
 404  968
     }
 405  
 
 406  
     /**
 407  
      * Create the default diagrams for the project. Currently a Class Diagram
 408  
      * and a UseCase diagram.
 409  
      * 
 410  
      * @param project the project to create the diagrams in.
 411  
      */
 412  
     private void createDefaultDiagrams(Project project) {
 413  954
         LOG.debug("Creating default diagrams");
 414  954
         Object model = project.getRoots().iterator().next();
 415  954
         DiagramFactory df = DiagramFactory.getInstance();
 416  954
         ArgoDiagram d = createClassDiagram(project);
 417  954
         LOG.debug("Creating use case diagram");
 418  954
         project.addMember(df.create(
 419  
                 DiagramFactory.DiagramType.UseCase, model, 
 420  
                 project.getProjectSettings().getDefaultDiagramSettings()));
 421  954
         project.addMember(new ProjectMemberTodoList("",
 422  
                 project));
 423  954
         createTodoList(project);
 424  954
         project.setActiveDiagram(d);
 425  954
     }
 426  
 
 427  
     /**
 428  
      * Create a class diagrams for the project.
 429  
      * 
 430  
      * @param project the project to create the diagram in.
 431  
      * @return the created class diagram
 432  
      */
 433  
     private ArgoDiagram createClassDiagram(Project project) {
 434  968
         LOG.debug("Creating class diagram");
 435  968
         Object model = project.getRoots().iterator().next();
 436  968
         DiagramFactory df = DiagramFactory.getInstance();
 437  968
         ArgoDiagram d = df.create(DiagramFactory.DiagramType.Class,
 438  
                 model, 
 439  
                 project.getProjectSettings().getDefaultDiagramSettings());
 440  968
         project.addMember(d);
 441  968
         return d;
 442  
     }
 443  
 
 444  
     /**
 445  
      * Create a todo list for the project.
 446  
      * 
 447  
      * @param project the project to create the todo list in.
 448  
      */
 449  
     private void createTodoList(Project project) {
 450  968
         LOG.debug("Creating todo list");
 451  968
         project.addMember(new ProjectMemberTodoList("",
 452  
                 project));
 453  968
     }
 454  
 
 455  
     /**
 456  
      * Create the top level model for the project and set it as a root and the
 457  
      * current namespace.
 458  
      * 
 459  
      * @param project the project to create the model in.
 460  
      */
 461  
     private void createDefaultModel(Project project) {
 462  954
         Object model = Model.getModelManagementFactory().createModel();
 463  954
         Model.getCoreHelper().setName(model,
 464  
                 Translator.localize("misc.untitled-model"));
 465  954
         Collection roots = new ArrayList();
 466  954
         roots.add(model);
 467  954
         project.setRoots(roots);
 468  954
         project.setCurrentNamespace(model);
 469  954
         project.addMember(model);
 470  
         // finally, apply profile configuration to the model
 471  954
         applyProfileConfiguration(project, model);
 472  954
     }
 473  
 
 474  
     /**
 475  
      * Create the top level profile for the project and set it as a root and the
 476  
      * current namespace.
 477  
      * 
 478  
      * @param project the project to create the model in.
 479  
      */
 480  
     private void createDefaultProfile(Project project) {
 481  14
         Object model = Model.getModelManagementFactory().createProfile();
 482  14
         Model.getCoreHelper().setName(model,
 483  
                 Translator.localize("misc.untitled-profile"));
 484  14
         Collection roots = new ArrayList();
 485  14
         roots.add(model);
 486  14
         project.setRoots(roots);
 487  14
         project.setCurrentNamespace(model);
 488  14
         project.addMember(model);
 489  
         // finally, apply profile configuration to the model
 490  14
         applyProfileConfiguration(project, model);
 491  14
     }
 492  
     
 493  
     /**
 494  
      * Set the save action.
 495  
      * 
 496  
      * @param save the action to be used
 497  
      */
 498  
     public void setSaveAction(Action save) {
 499  900
         this.saveAction = save;
 500  
         // Register with the save action with other subsystems so that
 501  
         // any changes in those subsystems will enable the
 502  
         // save button/menu item etc.
 503  900
         Designer.setSaveAction(save);
 504  900
     }
 505  
     
 506  
     /**
 507  
      * @return true is the save action is currently enabled
 508  
      * <p>
 509  
      * @deprecated for 0.27.2 by tfmorris.  Use {@link Project#isDirty()}.
 510  
      */
 511  
     public boolean isSaveActionEnabled() {
 512  0
         return this.saveAction.isEnabled();
 513  
     }
 514  
 
 515  
     /**
 516  
      * Notify the gui that the
 517  
      * current project's save state has changed. There are 2 receivers:
 518  
      * the SaveProject tool icon and the title bar (for showing a *).
 519  
      * <p>
 520  
      * @deprecated for 0.27.2 by tfmorris.  Use 
 521  
      * {@link Project#setDirty(boolean)}.
 522  
      */
 523  
     public void setSaveEnabled(boolean newValue) {
 524  4412
         if (saveAction != null) {
 525  4412
             saveAction.setEnabled(newValue);
 526  
         }
 527  4412
     }
 528  
 
 529  
     private void addProject(Project newProject) {
 530  1868
         openProjects.addLast(newProject);
 531  1868
     }
 532  
     
 533  
     /**
 534  
      * Remove the project.
 535  
      *
 536  
      * @param oldProject The project to be removed.
 537  
      */
 538  
     public void removeProject(Project oldProject) {
 539  0
         openProjects.remove(oldProject);
 540  
         
 541  
         // TODO: This code can be removed when getCurrentProject is removed
 542  0
         if (currentProject == oldProject) {
 543  0
             if (openProjects.size() > 0) {
 544  0
                 currentProject = openProjects.getLast();
 545  
             } else {
 546  0
                 currentProject = null;
 547  
             }
 548  
         }
 549  0
         oldProject.remove();
 550  0
     }
 551  
 
 552  
     /**
 553  
      * Updates the top level ModelElements for all projects.
 554  
      */
 555  
     public void updateRoots() {
 556  0
         if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
 557  
             // not needed in UML 1.x
 558  0
             return;
 559  
         }
 560  0
         for (Project p : getOpenProjects()) {
 561  0
             p.updateRoots();
 562  
         }
 563  0
         firePropertyChanged(OPEN_PROJECTS_PROPERTY,
 564  
                 new Project[] {currentProject}, new Project[] {currentProject});
 565  0
     }
 566  
 
 567  
     /**
 568  
      * Called when the model subsystem creates a command.
 569  
      * We must add this to the UndoManager.
 570  
      *
 571  
      * @param command the command.
 572  
      * @return result of the command, if any
 573  
      * @see org.argouml.model.ModelCommandCreationObserver#execute(ModelCommand)
 574  
      */
 575  
     public Object execute(final ModelCommand command) {
 576  1085
         setSaveEnabled(true);
 577  1085
         AbstractCommand wrappedCommand = new AbstractCommand() {
 578  1085
             private ModelCommand modelCommand = command;
 579  
             public void undo() {
 580  0
                 modelCommand.undo();
 581  0
             }
 582  
             public boolean isUndoable() {
 583  0
                 return modelCommand.isUndoable();
 584  
             }
 585  
             public boolean isRedoable() {
 586  0
                 return modelCommand.isRedoable();
 587  
             }
 588  
             public Object execute() {
 589  1085
                 return modelCommand.execute();
 590  
             }
 591  
             public String toString() {
 592  0
                 return modelCommand.toString();
 593  
             }
 594  
         };
 595  1085
         Project p = getCurrentProject();
 596  1085
         if (p != null) {
 597  185
             return getCurrentProject().getUndoManager().execute(wrappedCommand);
 598  
         } else {
 599  900
             return wrappedCommand.execute();
 600  
         }
 601  
     }
 602  
 }