Changeset 11224 in josm for trunk


Ignore:
Timestamp:
2016-11-08T23:29:31+01:00 (3 years ago)
Author:
michael2402
Message:

Simplify ExpertToggleAction

Make ExpertToggleAction use the listener list to track weak listeners and add a method to set expert mode explicitly. Use it in OsmDataLayerTest to make tracing expert mode problems easier.

Add test case for ExpertToggleAction.

Location:
trunk
Files:
1 added
3 edited

Legend:

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

    r10600 r11224  
    66import java.awt.Component;
    77import java.awt.event.ActionEvent;
    8 import java.lang.ref.WeakReference;
    9 import java.util.ArrayList;
    10 import java.util.Iterator;
    11 import java.util.List;
    128
    139import org.openstreetmap.josm.Main;
     10import org.openstreetmap.josm.data.preferences.BooleanProperty;
     11import org.openstreetmap.josm.tools.ListenerList;
    1412
    1513/**
     
    1917public class ExpertToggleAction extends ToggleAction {
    2018
     19    /**
     20     * This listener is notified whenever the expert mode setting changed.
     21     */
    2122    @FunctionalInterface
    2223    public interface ExpertModeChangeListener {
     24        /**
     25         * The expert mode changed.
     26         * @param isExpert <code>true</code> if expert mode was enabled, false otherwise.
     27         */
    2328        void expertChanged(boolean isExpert);
    2429    }
    2530
    26     private static final List<WeakReference<ExpertModeChangeListener>> listeners = new ArrayList<>();
    27     private static final List<WeakReference<Component>> visibilityToggleListeners = new ArrayList<>();
     31    // TODO: Switch to checked list. We can do this as soon as we do not see any more warnings.
     32    private static final ListenerList<ExpertModeChangeListener> listeners = ListenerList.createUnchecked();
     33    private static final ListenerList<Component> visibilityToggleListeners = ListenerList.createUnchecked();
     34
     35    private static final BooleanProperty PREF_EXPERT = new BooleanProperty("expert", false);
    2836
    2937    private static final ExpertToggleAction INSTANCE = new ExpertToggleAction();
    3038
    3139    private static synchronized void fireExpertModeChanged(boolean isExpert) {
    32         Iterator<WeakReference<ExpertModeChangeListener>> it1 = listeners.iterator();
    33         while (it1.hasNext()) {
    34             WeakReference<ExpertModeChangeListener> wr = it1.next();
    35             ExpertModeChangeListener listener = wr.get();
    36             if (listener == null) {
    37                 it1.remove();
    38                 continue;
    39             }
    40             listener.expertChanged(isExpert);
    41         }
    42         Iterator<WeakReference<Component>> it2 = visibilityToggleListeners.iterator();
    43         while (it2.hasNext()) {
    44             WeakReference<Component> wr = it2.next();
    45             Component c = wr.get();
    46             if (c == null) {
    47                 it2.remove();
    48                 continue;
    49             }
    50             c.setVisible(isExpert);
    51         }
     40        listeners.fireEvent(listener -> listener.expertChanged(isExpert));
     41        visibilityToggleListeners.fireEvent(c -> c.setVisible(isExpert));
    5242    }
    5343
     
    6353    public static synchronized void addExpertModeChangeListener(ExpertModeChangeListener listener, boolean fireWhenAdding) {
    6454        if (listener == null) return;
    65         for (WeakReference<ExpertModeChangeListener> wr : listeners) {
    66             // already registered ? => abort
    67             if (wr.get() == listener) return;
    68         }
    69         listeners.add(new WeakReference<>(listener));
     55        listeners.addWeakListener(listener);
    7056        if (fireWhenAdding) {
    7157            listener.expertChanged(isExpert());
     
    8066    public static synchronized void removeExpertModeChangeListener(ExpertModeChangeListener listener) {
    8167        if (listener == null) return;
    82         Iterator<WeakReference<ExpertModeChangeListener>> it = listeners.iterator();
    83         while (it.hasNext()) {
    84             WeakReference<ExpertModeChangeListener> wr = it.next();
    85             // remove the listener - and any other listener which god garbage
    86             // collected in the meantime
    87             if (wr.get() == null || wr.get() == listener) {
    88                 it.remove();
    89             }
    90         }
     68        listeners.removeListener(listener);
    9169    }
    9270
     71    /**
     72     * Marks a component to be only visible when expert mode is enabled. The visibility of the component is changed automatically.
     73     * @param c The component.
     74     */
    9375    public static synchronized void addVisibilitySwitcher(Component c) {
    9476        if (c == null) return;
    95         for (WeakReference<Component> wr : visibilityToggleListeners) {
    96             // already registered ? => abort
    97             if (wr.get() == c) return;
    98         }
    99         visibilityToggleListeners.add(new WeakReference<>(c));
     77        visibilityToggleListeners.addWeakListener(c);
    10078        c.setVisible(isExpert());
    10179    }
    10280
     81    /**
     82     * Stops tracking visibility changes for the given component.
     83     * @param c The component.
     84     * @see #addVisibilitySwitcher(Component)
     85     */
    10386    public static synchronized void removeVisibilitySwitcher(Component c) {
    10487        if (c == null) return;
    105         Iterator<WeakReference<Component>> it = visibilityToggleListeners.iterator();
    106         while (it.hasNext()) {
    107             WeakReference<Component> wr = it.next();
    108             // remove the listener - and any other listener which god garbage
    109             // collected in the meantime
    110             if (wr.get() == null || wr.get() == c) {
    111                 it.remove();
    112             }
    113         }
     88        visibilityToggleListeners.removeListener(c);
    11489    }
    11590
     
    128103            Main.toolbar.register(this);
    129104        }
    130         setSelected(Main.pref.getBoolean("expert", false));
     105        setSelected(PREF_EXPERT.get());
    131106        notifySelectedState();
    132107    }
     
    135110    protected final void notifySelectedState() {
    136111        super.notifySelectedState();
     112        PREF_EXPERT.put(isSelected());
    137113        fireExpertModeChanged(isSelected());
     114    }
     115
     116    /**
     117     * Forces the expert mode state to the given state.
     118     * @param isExpert if expert mode should be used.
     119     * @since 11224
     120     */
     121    public void setExpert(boolean isExpert) {
     122        if (isSelected() != isExpert) {
     123            setSelected(isExpert);
     124            notifySelectedState();
     125        }
    138126    }
    139127
     
    141129    public void actionPerformed(ActionEvent e) {
    142130        toggleSelectedState(e);
    143         Main.pref.put("expert", isSelected());
    144131        notifySelectedState();
    145132    }
  • trunk/src/org/openstreetmap/josm/tools/ListenerList.java

    r11018 r11224  
    8080     */
    8181    public synchronized void addWeakListener(T listener) {
    82         ensureNotInList(listener);
    83         // clean the weak listeners, just to be sure...
    84         while (weakListeners.remove(new WeakListener<T>(null))) {
    85             // continue
    86         }
    87         weakListeners.add(new WeakListener<>(listener));
     82        if (ensureNotInList(listener)) {
     83            // clean the weak listeners, just to be sure...
     84            while (weakListeners.remove(new WeakListener<T>(null))) {
     85                // continue
     86            }
     87            weakListeners.add(new WeakListener<>(listener));
     88        }
    8889    }
    8990
     
    9394     */
    9495    public synchronized void addListener(T listener) {
    95         ensureNotInList(listener);
    96         listeners.add(listener);
    97     }
    98 
    99     private void ensureNotInList(T listener) {
     96        if (ensureNotInList(listener)) {
     97            listeners.add(listener);
     98        }
     99    }
     100
     101    private boolean ensureNotInList(T listener) {
    100102        CheckParameterUtil.ensureParameterNotNull(listener, "listener");
    101103        if (containsListener(listener)) {
    102104            failAdd(listener);
     105            return false;
     106        } else {
     107            return true;
    103108        }
    104109    }
     
    217222    }
    218223
     224    private static class UncheckedListenerList<T> extends ListenerList<T> {
     225        @Override
     226        protected void failAdd(T listener) {
     227            Logging.warn("Listener was alreaady added: {0}", listener);
     228            // ignore
     229        }
     230
     231        @Override
     232        protected void failRemove(T listener) {
     233            Logging.warn("Listener was removed twice or not added: {0}", listener);
     234            // ignore
     235        }
     236    }
     237
    219238    /**
    220239     * Create a new listener list
     
    229248        }
    230249    }
     250
     251    /**
     252     * Creates a new listener list that does not fail if listeners are added ore removed twice.
     253     * <p>
     254     * Use of this list is discouraged. You should always use {@link #create()} in new implementations and check your listeners.
     255     * @param <T> The listener type
     256     * @return A new list.
     257     * @since 11224
     258     */
     259    public static <T> ListenerList<T> createUnchecked() {
     260        return new UncheckedListenerList<>();
     261    }
    231262}
  • trunk/test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java

    r11040 r11224  
    44import static org.junit.Assert.assertEquals;
    55import static org.junit.Assert.assertFalse;
    6 import static org.junit.Assert.assertNotEquals;
    76import static org.junit.Assert.assertNotNull;
    87import static org.junit.Assert.assertTrue;
     
    192191    public void testGetMenuEntries() {
    193192        OsmDataLayer layer = new OsmDataLayer(new DataSet(), "", null);
    194         boolean mode = ExpertToggleAction.isExpert();
    195         ExpertToggleAction.getInstance().actionPerformed(null);
    196         assertNotEquals(mode, ExpertToggleAction.isExpert());
    197         assertEquals(ExpertToggleAction.isExpert() ? 16 : 13, layer.getMenuEntries().length);
    198         ExpertToggleAction.getInstance().actionPerformed(null);
    199         assertEquals(mode, ExpertToggleAction.isExpert());
    200         assertEquals(ExpertToggleAction.isExpert() ? 16 : 13, layer.getMenuEntries().length);
     193        ExpertToggleAction.getInstance().setExpert(true);
     194        assertEquals(16, layer.getMenuEntries().length);
     195
     196        ExpertToggleAction.getInstance().setExpert(false);
     197        assertEquals(13, layer.getMenuEntries().length);
    201198    }
    202199
Note: See TracChangeset for help on using the changeset viewer.