Author: tfmorris
Date: 2008-09-06 14:07:04-0700
New Revision: 15681

Modified:
   trunk/src/argouml-app/src/org/argouml/application/events/ArgoEventPump.java
   trunk/src/argouml-app/src/org/argouml/kernel/ProjectManager.java
   trunk/src/argouml-app/src/org/argouml/ui/ProjectBrowser.java
   trunk/src/argouml-app/src/org/argouml/uml/ui/ActionSaveProject.java

Log:
RESOLVED - task 5368: ConcurrentModificationException on Sequence Diagram load 
http://argouml.tigris.org/issues/show_bug.cgi?id=5368

Modified: 
trunk/src/argouml-app/src/org/argouml/application/events/ArgoEventPump.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/application/events/ArgoEventPump.java?view=diff&rev=15681&p1=trunk/src/argouml-app/src/org/argouml/application/events/ArgoEventPump.java&p2=trunk/src/argouml-app/src/org/argouml/application/events/ArgoEventPump.java&r1=15680&r2=15681
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/application/events/ArgoEventPump.java 
(original)
+++ trunk/src/argouml-app/src/org/argouml/application/events/ArgoEventPump.java 
2008-09-06 14:07:04-0700
@@ -27,12 +27,18 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.swing.SwingUtilities;
+
 import org.apache.log4j.Logger;
 import org.argouml.application.api.ArgoEventListener;
 
 /**
- * ArgoEventPump is an eventhandler which handles events regarding
- * the loading and unloading of modules.
+ * ArgoEventPump is an event dispatcher which handles events that are global
+ * in nature for the entire application.
+ * <p>
+ * TODO: DiagramAppearance and Notation events are not application-wide and 
will
+ * be moved from here to someplace more specific in the future so that they can
+ * be managed on a per-project or per-diagram basis.
  */
 public final class ArgoEventPump {
     /**
@@ -147,13 +153,32 @@
 
     /**
      * Handle firing a notation event.
+     * <p>
+     * TODO: This needs to be managed on a per-diagram or per-project basis.
      *
      * @param event The event to be fired.
      * @param listener The listener.
      */
     private void handleFireNotationEvent(
-        ArgoNotationEvent event,
-        ArgoNotationEventListener listener) {
+        final ArgoNotationEvent event,
+        final ArgoNotationEventListener listener) {
+       
+        // Notation events are likely to cause GEF/Swing operations, so we
+        // dispatch them on the Swing event thread as a convenience so that 
+        // the receiving notationChanged() methods don't need to deal with it
+        if (SwingUtilities.isEventDispatchThread()) {
+            fireNotationEventInternal(event, listener);
+        } else {
+            SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                    fireNotationEventInternal(event, listener);
+                }
+            });
+        }
+    }
+
+    private void fireNotationEventInternal(ArgoNotationEvent event,
+            ArgoNotationEventListener listener) {
         switch (event.getEventType()) {
        case ArgoEventTypes.NOTATION_CHANGED :
            listener.notationChanged(event);
@@ -186,13 +211,29 @@
 
     /**
      * Handle firing a diagram appearance event.
-     *
+     * <p>
+     * TODO: This needs to be managed on a per-diagram or per-project basis.
+     * 
      * @param event The event to be fired.
      * @param listener The listener.
      */
     private void handleFireDiagramAppearanceEvent(
-        ArgoDiagramAppearanceEvent event,
-        ArgoDiagramAppearanceEventListener listener) {
+        final ArgoDiagramAppearanceEvent event,
+        final ArgoDiagramAppearanceEventListener listener) {
+        if (SwingUtilities.isEventDispatchThread()) {
+            fireDiagramAppearanceEventInternal(event, listener);
+        } else {
+            SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                    fireDiagramAppearanceEventInternal(event, listener);
+                }
+            });
+        }        
+    }
+
+    private void fireDiagramAppearanceEventInternal(
+            final ArgoDiagramAppearanceEvent event,
+            final ArgoDiagramAppearanceEventListener listener) {
         switch (event.getEventType()) {
         case ArgoEventTypes.DIAGRAM_FONT_CHANGED :
             listener.diagramFontChanged(event);

Modified: trunk/src/argouml-app/src/org/argouml/kernel/ProjectManager.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/kernel/ProjectManager.java?view=diff&rev=15681&p1=trunk/src/argouml-app/src/org/argouml/kernel/ProjectManager.java&p2=trunk/src/argouml-app/src/org/argouml/kernel/ProjectManager.java&r1=15680&r2=15681
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/kernel/ProjectManager.java    
(original)
+++ trunk/src/argouml-app/src/org/argouml/kernel/ProjectManager.java    
2008-09-06 14:07:04-0700
@@ -215,12 +215,21 @@
     }
 
     /**
-     * Returns the current project.<p>
-     *
-     * If there is no project, a new one is created
-     * (unless we are busy creating one).
-     *
-     * @return Project the current project
+     * Returns the current project (ie the project which must recently had the
+     * user focus) or null if there is no current project.
+     * <p>
+     * This should only be used by callers who need to know the global state.
+     * Most things which need a project want the project that contains them,
+     * which they can discover by traversing their containing elements (e.g.
+     * Fig->Diagram->Project).
+     * <p>
+     * <em>NOTE:</em>Callers of this method must be prepared to receive a null
+     * return value. Currently, if there is no project, a new one is created
+     * (unless we are busy creating one), but this behavior is not guaranteed
+     * and will change ArgoUML allows multiple open projects (or no open
+     * projects).
+     * 
+     * @return Project the current project or null if none
      */
     public Project getCurrentProject() {
         if (currentProject == null && !creatingCurrentProject) {
@@ -315,6 +324,8 @@
     
     /**
      * @return true is the save action is currently enabled
+     * <p>
+     * TODO: This needs to get the save-enabled status for the current project.
      */
     public boolean isSaveActionEnabled() {
         return this.saveAction.isEnabled();
@@ -324,7 +335,8 @@
      * Notify the gui that the
      * current project's save state has changed. There are 2 receivers:
      * the SaveProject tool icon and the title bar (for showing a *).
-     *
+     * <p>
+     * TODO: This needs to be managed on a per-project basis.
      * @param newValue The new state.
      */
     public void setSaveEnabled(boolean newValue) {
@@ -353,6 +365,7 @@
      * We must add this to the UndoManager.
      *
      * @param command the command.
+     * @return result of the command, if any
      * @see 
org.argouml.model.ModelCommandCreationObserver#execute(ModelCommand)
      */
     public Object execute(final ModelCommand command) {

Modified: trunk/src/argouml-app/src/org/argouml/ui/ProjectBrowser.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/ui/ProjectBrowser.java?view=diff&rev=15681&p1=trunk/src/argouml-app/src/org/argouml/ui/ProjectBrowser.java&p2=trunk/src/argouml-app/src/org/argouml/ui/ProjectBrowser.java&r1=15680&r2=15681
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/ui/ProjectBrowser.java        
(original)
+++ trunk/src/argouml-app/src/org/argouml/ui/ProjectBrowser.java        
2008-09-06 14:07:04-0700
@@ -333,7 +333,9 @@
                     // rid of this.
                     Project p = 
                         ProjectManager.getManager().getCurrentProject();
-                    p.getUndoManager().startInteraction("Focus");
+                    if (p != null) {
+                        p.getUndoManager().startInteraction("Focus");
+                    }
                     /* This next line is ideal for debugging the taborder
                      * (focus traversal), see e.g. issue 1849.
                      */
@@ -739,7 +741,7 @@
     }
     /**
      * Set the save indicator (the * after the title) to appear depending on
-     * the curreny save action enabled status.
+     * the current save action enabled status.
      */
     public void showSaveIndicator() {
         titleHandler.buildTitle(null, null);
@@ -1574,6 +1576,16 @@
 
         PersistenceManager pm = PersistenceManager.getInstance();
         Project oldProject = ProjectManager.getManager().getCurrentProject();
+        if (oldProject != null) {
+            // Remove the old project first.  It's wasteful to create a temp
+            // empty project, but too much of ArgoUML depends on having a
+            // current project
+            Project p = ProjectManager.getManager().makeEmptyProject();
+            ProjectManager.getManager().setCurrentProject(p);
+            ProjectManager.getManager().removeProject(oldProject);
+            oldProject = p;
+        }
+        
         boolean success = false;
 
         // TODO:
@@ -1598,7 +1610,7 @@
             // * appearing in title bar and the save enabling as models are
             // updated
             // TODO: Do we still need this now the save enablement is improved?
-            AbstractAction rememberedSaveAction = this.saveAction;
+            final AbstractAction rememberedSaveAction = this.saveAction;
             this.saveAction = null;
             ProjectManager.getManager().setSaveAction(null);
             try {
@@ -1747,11 +1759,17 @@
                 } finally {
                     // Make sure save action is always reinstated
                     this.saveAction = rememberedSaveAction;
-                    ProjectManager.getManager().setSaveAction(
-                            rememberedSaveAction);
-                    if (success) {
-                        rememberedSaveAction.setEnabled(false);
-                    }
+                    
+                    // We clear the save-required flag on the Swing event 
thread
+                    // in the hopes that it gets done after any other 
background
+                    // work (listener updates) that is being done there
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            ProjectManager.getManager().setSaveAction(
+                                    rememberedSaveAction);
+                            rememberedSaveAction.setEnabled(false);
+                        }
+                    });
                 }
             }
         }

Modified: trunk/src/argouml-app/src/org/argouml/uml/ui/ActionSaveProject.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/uml/ui/ActionSaveProject.java?view=diff&rev=15681&p1=trunk/src/argouml-app/src/org/argouml/uml/ui/ActionSaveProject.java&p2=trunk/src/argouml-app/src/org/argouml/uml/ui/ActionSaveProject.java&r1=15680&r2=15681
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/uml/ui/ActionSaveProject.java 
(original)
+++ trunk/src/argouml-app/src/org/argouml/uml/ui/ActionSaveProject.java 
2008-09-06 14:07:04-0700
@@ -82,16 +82,28 @@
     }
 
     /**
-     * Set the enabled state of the save action.
-     * When we become enabled inform the user by highlighting the title bar
-     * with an asterisk.
-     * This method is undoable.
+     * Set the enabled state of the save action. When we become enabled inform
+     * the user by highlighting the title bar with an asterisk. This method is
+     * undoable.  This method is synchronized so that it can be used from any
+     * thread without external synchronization.
+     * 
      * @param isEnabled new state for save command
      */
-    public void setEnabled(final boolean isEnabled) {
+    @Override
+    public synchronized void setEnabled(final boolean isEnabled) {
         if (isEnabled == this.enabled) {
             return;
         }
+        if (LOG.isDebugEnabled()) {
+            if (!enabled && isEnabled) {
+                Throwable throwable = new Throwable();
+                throwable.fillInStackTrace();
+                LOG.debug("Save action enabled by  ", throwable);
+            } else {
+                LOG.debug("Save state changed from " + enabled + " to "
+                        + isEnabled);
+            }
+        }
         internalSetEnabled(isEnabled);
     }
     
@@ -104,4 +116,4 @@
         ProjectBrowser.getInstance().showSaveIndicator();
     }
 
-} /* end class ActionSaveProject */
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to