Changeset 13729 in josm


Ignore:
Timestamp:
2018-05-11T23:39:43+02:00 (6 weeks ago)
Author:
Don-vip
Message:

fix #16260 - fix major performance problems seen during first JOSM launch:

  • new API to notify about undo/redo changes
  • use this API in Command Stack dialog to alter lightly the undo/redo trees instead of rebuilding them entirely at each command (very costly operation for large trees)
  • purge the command stack after computing the left/right traffic data to avoid starting JOSM with already many thousands nodes in undo/redo trees
  • avoid memory leak with JoinAreasAction when computing the left/right traffic data
Location:
trunk/src/org/openstreetmap/josm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java

    r13635 r13729  
    456456
    457457    /**
    458      * Constructs a new {@code JoinAreasAction} with optional shortcut.
    459      * @param addShortcut controls whether the shortcut should be registered or not, as for toolbar registration
     458     * Constructs a new {@code JoinAreasAction} with optional shortcut and adapters.
     459     * @param addShortcutToolbarAdapters controls whether the shortcut should be registered or not, as for toolbar registration and adapters
    460460     * @since 11611
    461461     */
    462     public JoinAreasAction(boolean addShortcut) {
    463         super(tr("Join overlapping Areas"), "joinareas", tr("Joins areas that overlap each other"), addShortcut ?
     462    public JoinAreasAction(boolean addShortcutToolbarAdapters) {
     463        super(tr("Join overlapping Areas"), "joinareas", tr("Joins areas that overlap each other"), addShortcutToolbarAdapters ?
    464464        Shortcut.registerShortcut("tools:joinareas", tr("Tool: {0}", tr("Join overlapping Areas")), KeyEvent.VK_J, Shortcut.SHIFT)
    465         : null, addShortcut);
     465        : null, addShortcutToolbarAdapters, null, addShortcutToolbarAdapters);
    466466    }
    467467
  • trunk/src/org/openstreetmap/josm/data/UndoRedoHandler.java

    r13010 r13729  
    22package org.openstreetmap.josm.data;
    33
     4import java.util.EventObject;
    45import java.util.Iterator;
    56import java.util.LinkedList;
     7import java.util.Objects;
    68
    79import org.openstreetmap.josm.Main;
     
    3032
    3133    private final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<>();
     34    private final LinkedList<CommandQueuePreciseListener> preciseListenerCommands = new LinkedList<>();
    3235
    3336    /**
     
    3942
    4043    /**
    41      * A listener that gets notified of command queue (undo/redo) size changes.
     44     * A simple listener that gets notified of command queue (undo/redo) size changes.
     45     * @see CommandQueuePreciseListener
    4246     * @since 12718 (moved from {@code OsmDataLayer}
    4347     */
     
    5054         */
    5155        void commandChanged(int queueSize, int redoSize);
     56    }
     57
     58    /**
     59     * A listener that gets notified of command queue (undo/redo) operations individually.
     60     * @see CommandQueueListener
     61     * @since 13729
     62     */
     63    public interface CommandQueuePreciseListener {
     64
     65        /**
     66         * Notifies the listener about a new command added to the queue.
     67         * @param e event
     68         */
     69        void commandAdded(CommandAddedEvent e);
     70
     71        /**
     72         * Notifies the listener about commands being cleaned.
     73         * @param e event
     74         */
     75        void cleaned(CommandQueueCleanedEvent e);
     76
     77        /**
     78         * Notifies the listener about a command that has been undone.
     79         * @param e event
     80         */
     81        void commandUndone(CommandUndoneEvent e);
     82
     83        /**
     84         * Notifies the listener about a command that has been redone.
     85         * @param e event
     86         */
     87        void commandRedone(CommandRedoneEvent e);
     88    }
     89
     90    abstract static class CommandQueueEvent extends EventObject {
     91        protected CommandQueueEvent(UndoRedoHandler source) {
     92            super(Objects.requireNonNull(source));
     93        }
     94
     95        /**
     96         * Calls the appropriate method of the listener for this event.
     97         * @param listener dataset listener to notify about this event
     98         */
     99        abstract void fire(CommandQueuePreciseListener listener);
     100
     101        @Override
     102        public final UndoRedoHandler getSource() {
     103            return (UndoRedoHandler) super.getSource();
     104        }
     105    }
     106
     107    /**
     108     * Event fired after a command has been added to the command queue.
     109     * @since xxx
     110     */
     111    public static final class CommandAddedEvent extends CommandQueueEvent {
     112
     113        private final Command cmd;
     114
     115        private CommandAddedEvent(UndoRedoHandler source, Command cmd) {
     116            super(source);
     117            this.cmd = Objects.requireNonNull(cmd);
     118        }
     119
     120        /**
     121         * Returns the added command.
     122         * @return the added command
     123         */
     124        public Command getCommand() {
     125            return cmd;
     126        }
     127
     128        @Override
     129        void fire(CommandQueuePreciseListener listener) {
     130            listener.commandAdded(this);
     131        }
     132    }
     133
     134    /**
     135     * Event fired after the command queue has been cleaned.
     136     * @since xxx
     137     */
     138    public static final class CommandQueueCleanedEvent extends CommandQueueEvent {
     139
     140        private final DataSet ds;
     141
     142        private CommandQueueCleanedEvent(UndoRedoHandler source, DataSet ds) {
     143            super(source);
     144            this.ds = ds;
     145        }
     146
     147        /**
     148         * Returns the affected dataset.
     149         * @return the affected dataset, or null if the queue has been globally emptied
     150         */
     151        public DataSet getDataSet() {
     152            return ds;
     153        }
     154
     155        @Override
     156        void fire(CommandQueuePreciseListener listener) {
     157            listener.cleaned(this);
     158        }
     159    }
     160
     161    /**
     162     * Event fired after a command has been undone.
     163     * @since xxx
     164     */
     165    public static final class CommandUndoneEvent extends CommandQueueEvent {
     166
     167        private final Command cmd;
     168
     169        private CommandUndoneEvent(UndoRedoHandler source, Command cmd) {
     170            super(source);
     171            this.cmd = Objects.requireNonNull(cmd);
     172        }
     173
     174        /**
     175         * Returns the undone command.
     176         * @return the undone command
     177         */
     178        public Command getCommand() {
     179            return cmd;
     180        }
     181
     182        @Override
     183        void fire(CommandQueuePreciseListener listener) {
     184            listener.commandUndone(this);
     185        }
     186    }
     187
     188    /**
     189     * Event fired after a command has been redone.
     190     * @since xxx
     191     */
     192    public static final class CommandRedoneEvent extends CommandQueueEvent {
     193
     194        private final Command cmd;
     195
     196        private CommandRedoneEvent(UndoRedoHandler source, Command cmd) {
     197            super(source);
     198            this.cmd = Objects.requireNonNull(cmd);
     199        }
     200
     201        /**
     202         * Returns the redone command.
     203         * @return the redone command
     204         */
     205        public Command getCommand() {
     206            return cmd;
     207        }
     208
     209        @Override
     210        void fire(CommandQueuePreciseListener listener) {
     211            listener.commandRedone(this);
     212        }
    52213    }
    53214
     
    80241    /**
    81242     * Fires a commands change event after adding a command.
    82      */
    83     public void afterAdd() {
     243     * @param cmd command added
     244     */
     245    public void afterAdd(Command cmd) {
     246        if (cmd != null) {
     247            fireEvent(new CommandAddedEvent(this, cmd));
     248        }
    84249        fireCommandsChanged();
    85250    }
     
    91256    public synchronized void add(final Command c) {
    92257        addNoRedraw(c);
    93         afterAdd();
     258        afterAdd(c);
    94259    }
    95260
     
    117282                c.undoCommand();
    118283                redoCommands.addFirst(c);
     284                fireEvent(new CommandUndoneEvent(this, c));
    119285                if (commands.isEmpty()) {
    120286                    break;
     
    147313            c.executeCommand();
    148314            commands.add(c);
     315            fireEvent(new CommandRedoneEvent(this, c));
    149316            if (redoCommands.isEmpty()) {
    150317                break;
     
    163330    }
    164331
     332    private void fireEvent(CommandQueueEvent e) {
     333        preciseListenerCommands.forEach(e::fire);
     334    }
     335
    165336    /**
    166337     * Resets the undo/redo list.
     
    169340        redoCommands.clear();
    170341        commands.clear();
     342        fireEvent(new CommandQueueCleanedEvent(this, null));
    171343        fireCommandsChanged();
    172344    }
     
    194366        }
    195367        if (changed) {
     368            fireEvent(new CommandQueueCleanedEvent(this, dataSet));
    196369            fireCommandsChanged();
    197370        }
     
    208381    /**
    209382     * Adds a command queue listener.
    210      * @param l The commands queue listener to add
     383     * @param l The command queue listener to add
    211384     * @return {@code true} if the listener has been added, {@code false} otherwise
    212385     */
     
    214387        return listenerCommands.add(l);
    215388    }
     389
     390    /**
     391     * Removes a precise command queue listener.
     392     * @param l The precise command queue listener to remove
     393     * @since 13729
     394     */
     395    public void removeCommandQueuePreciseListener(CommandQueuePreciseListener l) {
     396        preciseListenerCommands.remove(l);
     397    }
     398
     399    /**
     400     * Adds a precise command queue listener.
     401     * @param l The precise command queue listener to add
     402     * @return {@code true} if the listener has been added, {@code false} otherwise
     403     * @since 13729
     404     */
     405    public boolean addCommandQueuePreciseListener(CommandQueuePreciseListener l) {
     406        return preciseListenerCommands.add(l);
     407    }
    216408}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java

    r12866 r13729  
    3333import javax.swing.tree.DefaultTreeCellRenderer;
    3434import javax.swing.tree.DefaultTreeModel;
     35import javax.swing.tree.MutableTreeNode;
    3536import javax.swing.tree.TreePath;
    3637import javax.swing.tree.TreeSelectionModel;
     
    3940import org.openstreetmap.josm.command.Command;
    4041import org.openstreetmap.josm.command.PseudoCommand;
    41 import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueueListener;
     42import org.openstreetmap.josm.data.UndoRedoHandler.CommandAddedEvent;
     43import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueueCleanedEvent;
     44import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueuePreciseListener;
     45import org.openstreetmap.josm.data.UndoRedoHandler.CommandRedoneEvent;
     46import org.openstreetmap.josm.data.UndoRedoHandler.CommandUndoneEvent;
    4247import org.openstreetmap.josm.data.osm.DataSet;
    4348import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    5661 * @since 94
    5762 */
    58 public class CommandStackDialog extends ToggleDialog implements CommandQueueListener {
     63public class CommandStackDialog extends ToggleDialog implements CommandQueuePreciseListener {
    5964
    6065    private final DefaultTreeModel undoTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
     
    6368    private final JTree undoTree = new JTree(undoTreeModel);
    6469    private final JTree redoTree = new JTree(redoTreeModel);
     70
     71    private DefaultMutableTreeNode undoRoot;
     72    private DefaultMutableTreeNode redoRoot;
    6573
    6674    private final transient UndoRedoSelectionListener undoSelectionListener;
     
    229237            listener.updateEnabledState();
    230238        }
    231         MainApplication.undoRedo.addCommandQueueListener(this);
     239        MainApplication.undoRedo.addCommandQueuePreciseListener(this);
    232240    }
    233241
     
    243251    @Override
    244252    public void hideNotify() {
    245         undoTreeModel.setRoot(new DefaultMutableTreeNode());
    246         redoTreeModel.setRoot(new DefaultMutableTreeNode());
    247         MainApplication.undoRedo.removeCommandQueueListener(this);
     253        undoTreeModel.setRoot(undoRoot = new DefaultMutableTreeNode());
     254        redoTreeModel.setRoot(redoRoot = new DefaultMutableTreeNode());
     255        MainApplication.undoRedo.removeCommandQueuePreciseListener(this);
    248256    }
    249257
     
    254262    private void buildTrees() {
    255263        setTitle(tr("Command Stack"));
    256         if (MainApplication.getLayerManager().getEditLayer() == null)
    257             return;
    258 
     264        buildUndoTree();
     265        buildRedoTree();
     266        ensureTreesConsistency();
     267    }
     268
     269    private void buildUndoTree() {
    259270        List<Command> undoCommands = MainApplication.undoRedo.commands;
    260         DefaultMutableTreeNode undoRoot = new DefaultMutableTreeNode();
     271        undoRoot = new DefaultMutableTreeNode();
    261272        for (int i = 0; i < undoCommands.size(); ++i) {
    262273            undoRoot.add(getNodeForCommand(undoCommands.get(i), i));
    263274        }
    264275        undoTreeModel.setRoot(undoRoot);
    265 
     276    }
     277
     278    private void buildRedoTree() {
    266279        List<Command> redoCommands = MainApplication.undoRedo.redoCommands;
    267         DefaultMutableTreeNode redoRoot = new DefaultMutableTreeNode();
     280        redoRoot = new DefaultMutableTreeNode();
    268281        for (int i = 0; i < redoCommands.size(); ++i) {
    269282            redoRoot.add(getNodeForCommand(redoCommands.get(i), i));
    270283        }
    271284        redoTreeModel.setRoot(redoRoot);
     285    }
     286
     287    private void ensureTreesConsistency() {
     288        List<Command> undoCommands = MainApplication.undoRedo.commands;
     289        List<Command> redoCommands = MainApplication.undoRedo.redoCommands;
    272290        if (redoTreeModel.getChildCount(redoRoot) > 0) {
    273291            redoTree.scrollRowToVisible(0);
     
    342360
    343361    @Override
    344     public void commandChanged(int queueSize, int redoSize) {
    345         if (!isVisible())
    346             return;
    347         buildTrees();
     362    public void cleaned(CommandQueueCleanedEvent e) {
     363        if (isVisible()) {
     364            buildTrees();
     365        }
     366    }
     367
     368    @Override
     369    public void commandAdded(CommandAddedEvent e) {
     370        if (isVisible()) {
     371            undoRoot.add(getNodeForCommand(e.getCommand(), undoRoot.getChildCount()));
     372            undoTreeModel.nodeStructureChanged(undoRoot);
     373            ensureTreesConsistency();
     374        }
     375    }
     376
     377    @Override
     378    public void commandUndone(CommandUndoneEvent e) {
     379        if (isVisible()) {
     380            swapNode(undoTreeModel, undoRoot, undoRoot.getChildCount() - 1, redoTreeModel, redoRoot, 0);
     381        }
     382    }
     383
     384    @Override
     385    public void commandRedone(CommandRedoneEvent e) {
     386        if (isVisible()) {
     387            swapNode(redoTreeModel, redoRoot, 0, undoTreeModel, undoRoot, undoRoot.getChildCount());
     388        }
     389    }
     390
     391    private void swapNode(DefaultTreeModel srcModel, DefaultMutableTreeNode srcRoot, int srcIndex,
     392                          DefaultTreeModel dstModel, DefaultMutableTreeNode dstRoot, int dstIndex) {
     393        MutableTreeNode node = (MutableTreeNode) srcRoot.getChildAt(srcIndex);
     394        srcRoot.remove(node);
     395        srcModel.nodeStructureChanged(srcRoot);
     396        dstRoot.insert(node, dstIndex);
     397        dstModel.nodeStructureChanged(dstRoot);
     398        ensureTreesConsistency();
    348399    }
    349400
  • trunk/src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java

    r13434 r13729  
    647647                monitor.subTask(tr("Updating map ..."));
    648648                SwingUtilities.invokeAndWait(() -> {
    649                     MainApplication.undoRedo.afterAdd();
     649                    MainApplication.undoRedo.afterAdd(null);
    650650                    invalidateValidatorLayers();
    651651                    tree.resetErrors();
  • trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java

    r13647 r13729  
    3030import org.openstreetmap.josm.data.osm.UploadPolicy;
    3131import org.openstreetmap.josm.data.osm.Way;
     32import org.openstreetmap.josm.gui.MainApplication;
    3233import org.openstreetmap.josm.io.IllegalDataException;
    3334import org.openstreetmap.josm.io.OsmReader;
     
    135136            optimizedWays.addAll(ways);
    136137        }
     138        // Clean command stack
     139        MainApplication.undoRedo.clean(data);
    137140        return optimizedWays;
    138141    }
Note: See TracChangeset for help on using the changeset viewer.