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]
