Ignore:
Timestamp:
2017-03-23T00:01:04+01:00 (7 years ago)
Author:
Don-vip
Message:

fix #14475 - Deadlock while starting unit tests: remove the need to construct a CombinePrimitiveDialog to detect conflicts

Location:
trunk/src/org/openstreetmap/josm/gui/conflict/tags
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java

    r11627 r11772  
    1212import java.awt.GraphicsEnvironment;
    1313import java.awt.event.ActionEvent;
    14 import java.awt.event.HierarchyBoundsListener;
    15 import java.awt.event.HierarchyEvent;
    1614import java.awt.event.WindowAdapter;
    1715import java.awt.event.WindowEvent;
     
    3533import org.openstreetmap.josm.Main;
    3634import org.openstreetmap.josm.actions.ExpertToggleAction;
    37 import org.openstreetmap.josm.command.ChangePropertyCommand;
    3835import org.openstreetmap.josm.command.Command;
    3936import org.openstreetmap.josm.data.osm.Node;
     
    4744import org.openstreetmap.josm.gui.help.HelpUtil;
    4845import org.openstreetmap.josm.gui.util.GuiHelper;
     46import org.openstreetmap.josm.gui.widgets.AutoAdjustingSplitPane;
    4947import org.openstreetmap.josm.tools.CheckParameterUtil;
    5048import org.openstreetmap.josm.tools.ImageProvider;
     
    6260 * Prior to {@link #launchIfNecessary}, the following usage sequence was needed:
    6361 *
    64  * There is a singleton instance of this dialog which can be retrieved using
    65  * {@link #getInstance()}.
    66  *
    6762 * The dialog uses two models: one  for resolving tag conflicts, the other
    6863 * for resolving conflicts in relation memberships. For both models there are accessors,
     
    7166 * Models have to be <strong>populated</strong> before the dialog is launched. Example:
    7267 * <pre>
    73  *    CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
     68 *    CombinePrimitiveResolverDialog dialog = new CombinePrimitiveResolverDialog(Main.parent);
    7469 *    dialog.getTagConflictResolverModel().populate(aTagCollection);
    7570 *    dialog.getRelationMemberConflictResolverModel().populate(aRelationLinkCollection);
     
    8782public class CombinePrimitiveResolverDialog extends JDialog {
    8883
    89     /** the unique instance of the dialog */
    90     private static CombinePrimitiveResolverDialog instance;
    91 
    92     /**
    93      * Replies the unique instance of the dialog
    94      *
    95      * @return the unique instance of the dialog
    96      * @deprecated use {@link #launchIfNecessary} instead.
    97      */
    98     @Deprecated
    99     public static synchronized CombinePrimitiveResolverDialog getInstance() {
    100         if (instance == null) {
    101             GuiHelper.runInEDTAndWait(() -> instance = new CombinePrimitiveResolverDialog(Main.parent));
    102         }
    103         return instance;
    104     }
    105 
    10684    private AutoAdjustingSplitPane spTagConflictTypes;
    107     private TagConflictResolver pnlTagConflictResolver;
     85    private final TagConflictResolverModel modelTagConflictResolver;
     86    protected TagConflictResolver pnlTagConflictResolver;
     87    private final RelationMemberConflictResolverModel modelRelConflictResolver;
    10888    protected RelationMemberConflictResolver pnlRelationMemberConflictResolver;
     89    private final CombinePrimitiveResolver primitiveResolver;
    10990    private boolean applied;
    11091    private JPanel pnlButtons;
     
    11798
    11899    /**
    119      * Replies the target primitive the collection of primitives is merged
    120      * or combined to.
     100     * Constructs a new {@code CombinePrimitiveResolverDialog}.
     101     * @param parent The parent component in which this dialog will be displayed.
     102     */
     103    public CombinePrimitiveResolverDialog(Component parent) {
     104        this(parent, new TagConflictResolverModel(), new RelationMemberConflictResolverModel());
     105    }
     106
     107    /**
     108     * Constructs a new {@code CombinePrimitiveResolverDialog}.
     109     * @param parent The parent component in which this dialog will be displayed.
     110     * @param tagModel tag conflict resolver model
     111     * @param relModel relation member conflict resolver model
     112     * @since 11772
     113     */
     114    public CombinePrimitiveResolverDialog(Component parent,
     115            TagConflictResolverModel tagModel, RelationMemberConflictResolverModel relModel) {
     116        super(GuiHelper.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
     117        this.modelTagConflictResolver = tagModel;
     118        this.modelRelConflictResolver = relModel;
     119        this.primitiveResolver = new CombinePrimitiveResolver(tagModel, relModel);
     120        build();
     121    }
     122
     123    /**
     124     * Replies the target primitive the collection of primitives is merged or combined to.
    121125     *
    122126     * @return the target primitive
    123      */
    124     public OsmPrimitive getTargetPrimitmive() {
     127     * @since 11772 (naming)
     128     */
     129    public OsmPrimitive getTargetPrimitive() {
    125130        return targetPrimitive;
    126131    }
     
    149154    }
    150155
     156    /**
     157     * Updates the dialog title.
     158     */
    151159    protected void updateTitle() {
    152160        if (targetPrimitive == null) {
     
    169177    }
    170178
     179    /**
     180     * Builds the components.
     181     */
    171182    protected final void build() {
    172183        getContentPane().setLayout(new BorderLayout());
     
    182193    }
    183194
     195    /**
     196     * Builds the tag conflict resolver panel.
     197     * @return the tag conflict resolver panel
     198     */
    184199    protected JPanel buildTagConflictResolverPanel() {
    185         pnlTagConflictResolver = new TagConflictResolver();
     200        pnlTagConflictResolver = new TagConflictResolver(modelTagConflictResolver);
    186201        return pnlTagConflictResolver;
    187202    }
    188203
     204    /**
     205     * Builds the relation member conflict resolver panel.
     206     * @return the relation member conflict resolver panel
     207     */
    189208    protected JPanel buildRelationMemberConflictResolverPanel() {
    190         pnlRelationMemberConflictResolver = new RelationMemberConflictResolver(new RelationMemberConflictResolverModel());
     209        pnlRelationMemberConflictResolver = new RelationMemberConflictResolver(modelRelConflictResolver);
    191210        return pnlRelationMemberConflictResolver;
    192211    }
    193212
     213    /**
     214     * Builds the "Apply" action.
     215     * @return the "Apply" action
     216     */
    194217    protected ApplyAction buildApplyAction() {
    195218        return new ApplyAction();
    196219    }
    197220
     221    /**
     222     * Builds the button panel.
     223     * @return the button panel
     224     */
    198225    protected JPanel buildButtonPanel() {
    199226        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
     
    201228        // -- apply button
    202229        ApplyAction applyAction = buildApplyAction();
    203         pnlTagConflictResolver.getModel().addPropertyChangeListener(applyAction);
    204         pnlRelationMemberConflictResolver.getModel().addPropertyChangeListener(applyAction);
     230        modelTagConflictResolver.addPropertyChangeListener(applyAction);
     231        modelRelConflictResolver.addPropertyChangeListener(applyAction);
    205232        btnApply = new JButton(applyAction);
    206233        btnApply.setFocusable(true);
     
    219246
    220247    /**
    221      * Constructs a new {@code CombinePrimitiveResolverDialog}.
    222      * @param parent The parent component in which this dialog will be displayed.
    223      */
    224     public CombinePrimitiveResolverDialog(Component parent) {
    225         super(GuiHelper.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
    226         build();
    227     }
    228 
    229     /**
    230248     * Replies the tag conflict resolver model.
    231249     * @return The tag conflict resolver model.
    232250     */
    233251    public TagConflictResolverModel getTagConflictResolverModel() {
    234         return pnlTagConflictResolver.getModel();
     252        return modelTagConflictResolver;
    235253    }
    236254
     
    240258     */
    241259    public RelationMemberConflictResolverModel getRelationMemberConflictResolverModel() {
    242         return pnlRelationMemberConflictResolver.getModel();
     260        return modelRelConflictResolver;
    243261    }
    244262
     
    249267     */
    250268    public boolean isResolvedCompletely() {
    251         return getTagConflictResolverModel().isResolvedCompletely()
    252                 && getRelationMemberConflictResolverModel().isResolvedCompletely();
    253     }
    254 
     269        return modelTagConflictResolver.isResolvedCompletely()
     270            && modelRelConflictResolver.isResolvedCompletely();
     271    }
     272
     273    /**
     274     * Builds the list of tag change commands.
     275     * @param primitive target primitive
     276     * @param tc all resolutions
     277     * @return the list of tag change commands
     278     */
    255279    protected List<Command> buildTagChangeCommand(OsmPrimitive primitive, TagCollection tc) {
    256         List<Command> cmds = new LinkedList<>();
    257         for (String key : tc.getKeys()) {
    258             if (tc.hasUniqueEmptyValue(key)) {
    259                 if (primitive.get(key) != null) {
    260                     cmds.add(new ChangePropertyCommand(primitive, key, null));
    261                 }
    262             } else {
    263                 String value = tc.getJoinedValues(key);
    264                 if (!value.equals(primitive.get(key))) {
    265                     cmds.add(new ChangePropertyCommand(primitive, key, value));
    266                 }
    267             }
    268         }
    269         return cmds;
     280        return primitiveResolver.buildTagChangeCommand(primitive, tc);
    270281    }
    271282
     
    275286     */
    276287    public List<Command> buildResolutionCommands() {
    277         List<Command> cmds = new LinkedList<>();
    278 
    279         TagCollection allResolutions = getTagConflictResolverModel().getAllResolutions();
    280         if (!allResolutions.isEmpty()) {
    281             cmds.addAll(buildTagChangeCommand(targetPrimitive, allResolutions));
    282         }
    283         for (String p : OsmPrimitive.getDiscardableKeys()) {
    284             if (targetPrimitive.get(p) != null) {
    285                 cmds.add(new ChangePropertyCommand(targetPrimitive, p, null));
    286             }
    287         }
    288 
    289         if (getRelationMemberConflictResolverModel().getNumDecisions() > 0) {
    290             cmds.addAll(getRelationMemberConflictResolverModel().buildResolutionCommands(targetPrimitive));
    291         }
    292 
    293         Command cmd = pnlRelationMemberConflictResolver.buildTagApplyCommands(getRelationMemberConflictResolverModel()
     288        List<Command> cmds = primitiveResolver.buildResolutionCommands(targetPrimitive);
     289        Command cmd = pnlRelationMemberConflictResolver.buildTagApplyCommands(modelRelConflictResolver
    294290                .getModifiedRelations(targetPrimitive));
    295291        if (cmd != null) {
     
    312308     */
    313309    private void prepareDefaultDecisions(boolean fireEvent) {
    314         getTagConflictResolverModel().prepareDefaultTagDecisions(fireEvent);
    315         getRelationMemberConflictResolverModel().prepareDefaultRelationDecisions(fireEvent);
    316     }
    317 
     310        modelTagConflictResolver.prepareDefaultTagDecisions(fireEvent);
     311        modelRelConflictResolver.prepareDefaultRelationDecisions(fireEvent);
     312    }
     313
     314    /**
     315     * Builds empty conflicts panel.
     316     * @return empty conflicts panel
     317     */
    318318    protected JPanel buildEmptyConflictsPanel() {
    319319        JPanel pnl = new JPanel(new BorderLayout());
     
    322322    }
    323323
     324    /**
     325     * Prepares GUI before conflict resolution starts.
     326     */
    324327    protected void prepareGUIBeforeConflictResolutionStarts() {
    325         RelationMemberConflictResolverModel relModel = getRelationMemberConflictResolverModel();
    326         TagConflictResolverModel tagModel = getTagConflictResolverModel();
    327328        getContentPane().removeAll();
    328329
    329         if (relModel.getNumDecisions() > 0 && tagModel.getNumDecisions() > 0) {
     330        if (modelRelConflictResolver.getNumDecisions() > 0 && modelTagConflictResolver.getNumDecisions() > 0) {
    330331            // display both, the dialog for resolving relation conflicts and for resolving tag conflicts
    331332            spTagConflictTypes.setTopComponent(pnlTagConflictResolver);
    332333            spTagConflictTypes.setBottomComponent(pnlRelationMemberConflictResolver);
    333334            getContentPane().add(spTagConflictTypes, BorderLayout.CENTER);
    334         } else if (relModel.getNumDecisions() > 0) {
     335        } else if (modelRelConflictResolver.getNumDecisions() > 0) {
    335336            // relation conflicts only
    336337            getContentPane().add(pnlRelationMemberConflictResolver, BorderLayout.CENTER);
    337         } else if (tagModel.getNumDecisions() > 0) {
     338        } else if (modelTagConflictResolver.getNumDecisions() > 0) {
    338339            // tag conflicts only
    339340            getContentPane().add(pnlTagConflictResolver, BorderLayout.CENTER);
     
    348349    }
    349350
     351    /**
     352     * Sets whether this dialog has been closed with "Apply".
     353     * @param applied {@code true} if this dialog has been closed with "Apply"
     354     */
    350355    protected void setApplied(boolean applied) {
    351356        this.applied = applied;
     
    375380    }
    376381
    377     class CancelAction extends AbstractAction {
    378 
    379         CancelAction() {
     382    /**
     383     * Cancel action.
     384     */
     385    protected class CancelAction extends AbstractAction {
     386
     387        /**
     388         * Constructs a new {@code CancelAction}.
     389         */
     390        public CancelAction() {
    380391            putValue(Action.SHORT_DESCRIPTION, tr("Cancel conflict resolution"));
    381392            putValue(Action.NAME, tr("Cancel"));
     
    390401    }
    391402
     403    /**
     404     * Apply action.
     405     */
    392406    protected class ApplyAction extends AbstractAction implements PropertyChangeListener {
    393407
     408        /**
     409         * Constructs a new {@code ApplyAction}.
     410         */
    394411        public ApplyAction() {
    395412            putValue(Action.SHORT_DESCRIPTION, tr("Apply resolved conflicts"));
     
    406423        }
    407424
     425        /**
     426         * Updates enabled state.
     427         */
    408428        protected final void updateEnabledState() {
    409             setEnabled(pnlTagConflictResolver.getModel().getNumConflicts() == 0
    410                     && pnlRelationMemberConflictResolver.getModel().getNumConflicts() == 0);
     429            setEnabled(modelTagConflictResolver.isResolvedCompletely()
     430                    && modelRelConflictResolver.isResolvedCompletely());
    411431        }
    412432
     
    423443
    424444    private void adjustDividerLocation() {
    425         int numTagDecisions = getTagConflictResolverModel().getNumDecisions();
    426         int numRelationDecisions = getRelationMemberConflictResolverModel().getNumDecisions();
     445        int numTagDecisions = modelTagConflictResolver.getNumDecisions();
     446        int numRelationDecisions = modelRelConflictResolver.getNumDecisions();
    427447        if (numTagDecisions > 0 && numRelationDecisions > 0) {
    428448            double nTop = 1.0 + numTagDecisions;
     
    436456        public void windowOpened(WindowEvent e) {
    437457            adjustDividerLocation();
    438         }
    439     }
    440 
    441     static class AutoAdjustingSplitPane extends JSplitPane implements PropertyChangeListener, HierarchyBoundsListener {
    442         private double dividerLocation;
    443 
    444         AutoAdjustingSplitPane(int newOrientation) {
    445             super(newOrientation);
    446             addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, this);
    447             addHierarchyBoundsListener(this);
    448         }
    449 
    450         @Override
    451         public void ancestorResized(HierarchyEvent e) {
    452             setDividerLocation((int) (dividerLocation * getHeight()));
    453         }
    454 
    455         @Override
    456         public void ancestorMoved(HierarchyEvent e) {
    457             // do nothing
    458         }
    459 
    460         @Override
    461         public void propertyChange(PropertyChangeEvent evt) {
    462             if (JSplitPane.DIVIDER_LOCATION_PROPERTY.equals(evt.getPropertyName())) {
    463                 int newVal = (Integer) evt.getNewValue();
    464                 if (getHeight() != 0) {
    465                     dividerLocation = (double) newVal / (double) getHeight();
    466                 }
    467             }
    468458        }
    469459    }
     
    512502        }
    513503
    514         List<Command> cmds = new LinkedList<>();
    515 
    516         if (!GraphicsEnvironment.isHeadless()) {
     504        final List<Command> cmds = new LinkedList<>();
     505
     506        final TagConflictResolverModel tagModel = new TagConflictResolverModel();
     507        final RelationMemberConflictResolverModel relModel = new RelationMemberConflictResolverModel();
     508
     509        tagModel.populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues(), false);
     510        relModel.populate(parentRelations, primitives, false);
     511        tagModel.prepareDefaultTagDecisions(false);
     512        relModel.prepareDefaultRelationDecisions(false);
     513
     514        if (tagModel.isResolvedCompletely() && relModel.isResolvedCompletely()) {
     515            // Build commands without need of dialog
     516            CombinePrimitiveResolver resolver = new CombinePrimitiveResolver(tagModel, relModel);
     517            for (OsmPrimitive i : targetPrimitives) {
     518                cmds.addAll(resolver.buildResolutionCommands(i));
     519            }
     520        } else if (!GraphicsEnvironment.isHeadless()) {
    517521            // Build conflict resolution dialog
    518             final CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
    519 
    520             dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues(), false);
    521             dialog.getRelationMemberConflictResolverModel().populate(parentRelations, primitives, false);
    522             dialog.prepareDefaultDecisions(false);
     522            final CombinePrimitiveResolverDialog dialog = new CombinePrimitiveResolverDialog(Main.parent, tagModel, relModel);
    523523
    524524            // Ensure a proper title is displayed instead of a previous target (fix #7925)
     
    529529            }
    530530
    531             // Resolve tag conflicts if necessary
    532             if (!dialog.isResolvedCompletely()) {
    533                 GuiHelper.runInEDTAndWait(() -> {
    534                     dialog.getTagConflictResolverModel().fireTableDataChanged();
    535                     dialog.getRelationMemberConflictResolverModel().fireTableDataChanged();
    536                     dialog.updateTitle();
    537                 });
    538                 dialog.setVisible(true);
    539                 if (!dialog.isApplied()) {
    540                     throw new UserCancelException();
    541                 }
    542             }
     531            // Resolve tag conflicts
     532            GuiHelper.runInEDTAndWait(() -> {
     533                tagModel.fireTableDataChanged();
     534                relModel.fireTableDataChanged();
     535                dialog.updateTitle();
     536            });
     537            dialog.setVisible(true);
     538            if (!dialog.isApplied()) {
     539                throw new UserCancelException();
     540            }
     541
     542            // Build commands
    543543            for (OsmPrimitive i : targetPrimitives) {
    544544                dialog.setTargetPrimitive(i, false);
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java

    r10791 r11772  
    5858    }
    5959
    60     private final TagConflictResolver allPrimitivesResolver = new TagConflictResolver();
     60    private final TagConflictResolverModel model = new TagConflictResolverModel();
    6161    private final transient Map<OsmPrimitiveType, TagConflictResolver> resolvers = new EnumMap<>(OsmPrimitiveType.class);
    6262    private final JTabbedPane tpResolvers = new JTabbedPane();
     
    8181        setTitle(tr("Conflicts in pasted tags"));
    8282        for (OsmPrimitiveType type: OsmPrimitiveType.dataValues()) {
    83             resolvers.put(type, new TagConflictResolver());
    84             resolvers.get(type).getModel().addPropertyChangeListener(this);
     83            TagConflictResolverModel tagModel = new TagConflictResolverModel();
     84            resolvers.put(type, new TagConflictResolver(tagModel));
     85            tagModel.addPropertyChangeListener(this);
    8586        }
    8687        getContentPane().setLayout(new GridBagLayout());
     
    106107        getContentPane().add(buildButtonPanel(), gc);
    107108        InputMapUtils.addEscapeAction(getRootPane(), new CancelAction());
    108 
    109109    }
    110110
     
    114114        // -- apply button
    115115        ApplyAction applyAction = new ApplyAction();
    116         allPrimitivesResolver.getModel().addPropertyChangeListener(applyAction);
     116        model.addPropertyChangeListener(applyAction);
    117117        for (TagConflictResolver r : resolvers.values()) {
    118118            r.getModel().addPropertyChangeListener(applyAction);
     
    141141     */
    142142    protected void initResolver(OsmPrimitiveType type, TagCollection tc, Map<OsmPrimitiveType, Integer> targetStatistics) {
    143         resolvers.get(type).getModel().populate(tc, tc.getKeysWithMultipleValues());
    144         resolvers.get(type).getModel().prepareDefaultTagDecisions();
     143        TagConflictResolver resolver = resolvers.get(type);
     144        resolver.getModel().populate(tc, tc.getKeysWithMultipleValues());
     145        resolver.getModel().prepareDefaultTagDecisions();
    145146        if (!tc.isEmpty() && targetStatistics.get(type) != null && targetStatistics.get(type) > 0) {
    146             tpResolvers.add(PANE_TITLES.get(type), resolvers.get(type));
     147            tpResolvers.add(PANE_TITLES.get(type), resolver);
    147148        }
    148149    }
     
    164165        // init the resolver
    165166        //
    166         allPrimitivesResolver.getModel().populate(tagsForAllPrimitives, tagsForAllPrimitives.getKeysWithMultipleValues());
    167         allPrimitivesResolver.getModel().prepareDefaultTagDecisions();
     167        model.populate(tagsForAllPrimitives, tagsForAllPrimitives.getKeysWithMultipleValues());
     168        model.prepareDefaultTagDecisions();
    168169
    169170        // prepare the dialog with one tag resolver
    170171        pnlTagResolver.removeAll();
    171         pnlTagResolver.add(allPrimitivesResolver, BorderLayout.CENTER);
     172        pnlTagResolver.add(new TagConflictResolver(model), BorderLayout.CENTER);
    172173
    173174        statisticsModel.reset();
     
    297298                setEnabled(false);
    298299            } else if (mode.equals(Mode.RESOLVING_ONE_TAGCOLLECTION_ONLY)) {
    299                 setEnabled(allPrimitivesResolver.getModel().isResolvedCompletely());
     300                setEnabled(model.isResolvedCompletely());
    300301            } else {
    301302                setEnabled(resolvers.values().stream().allMatch(val -> val.getModel().isResolvedCompletely()));
     
    329330     */
    330331    public TagCollection getResolution() {
    331         return allPrimitivesResolver.getModel().getResolution();
     332        return model.getResolution();
    332333    }
    333334
     
    340341    public void propertyChange(PropertyChangeEvent evt) {
    341342        if (evt.getPropertyName().equals(TagConflictResolverModel.NUM_CONFLICTS_PROP)) {
    342             TagConflictResolverModel model = (TagConflictResolverModel) evt.getSource();
     343            TagConflictResolverModel tagModel = (TagConflictResolverModel) evt.getSource();
    343344            for (int i = 0; i < tpResolvers.getTabCount(); i++) {
    344345                TagConflictResolver resolver = (TagConflictResolver) tpResolvers.getComponentAt(i);
    345                 if (model == resolver.getModel()) {
     346                if (tagModel == resolver.getModel()) {
    346347                    tpResolvers.setIconAt(i,
    347348                            (Integer) evt.getNewValue() == 0 ? iconResolved : iconUnresolved
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolver.java

    r10611 r11772  
    3131    /**
    3232     * Constructs a new {@code TagConflictResolver}.
     33     * @param model tag conflict resolver model
     34     * @since 11772
    3335     */
    34     public TagConflictResolver() {
    35         this.model = new TagConflictResolverModel();
     36    public TagConflictResolver(TagConflictResolverModel model) {
     37        this.model = model;
    3638        build();
    3739    }
Note: See TracChangeset for help on using the changeset viewer.