source: josm/trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java @ 12841

Last change on this file since 12841 was 12841, checked in by bastiK, 6 weeks ago

see #15229 - fix deprecations caused by [12840]

  • Property svn:eol-style set to native
File size: 20.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Color;
8import java.awt.Component;
9import java.awt.GridBagConstraints;
10import java.awt.GridBagLayout;
11import java.awt.Insets;
12import java.awt.event.ActionEvent;
13import java.awt.event.ActionListener;
14import java.awt.event.FocusAdapter;
15import java.awt.event.FocusEvent;
16import java.awt.event.ItemEvent;
17import java.awt.event.ItemListener;
18import java.beans.PropertyChangeEvent;
19import java.beans.PropertyChangeListener;
20import java.util.EnumMap;
21import java.util.Map;
22import java.util.Map.Entry;
23
24import javax.swing.BorderFactory;
25import javax.swing.ButtonGroup;
26import javax.swing.JLabel;
27import javax.swing.JPanel;
28import javax.swing.JRadioButton;
29import javax.swing.UIManager;
30import javax.swing.event.DocumentEvent;
31import javax.swing.event.DocumentListener;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
35import org.openstreetmap.josm.gui.widgets.JosmTextField;
36import org.openstreetmap.josm.io.Capabilities;
37import org.openstreetmap.josm.io.MaxChangesetSizeExceededPolicy;
38import org.openstreetmap.josm.io.OsmApi;
39import org.openstreetmap.josm.io.UploadStrategy;
40import org.openstreetmap.josm.io.UploadStrategySpecification;
41import org.openstreetmap.josm.tools.Logging;
42
43/**
44 * UploadStrategySelectionPanel is a panel for selecting an upload strategy.
45 *
46 * Clients can listen for property change events for the property
47 * {@link #UPLOAD_STRATEGY_SPECIFICATION_PROP}.
48 */
49public class UploadStrategySelectionPanel extends JPanel implements PropertyChangeListener {
50
51    /**
52     * The property for the upload strategy
53     */
54    public static final String UPLOAD_STRATEGY_SPECIFICATION_PROP =
55        UploadStrategySelectionPanel.class.getName() + ".uploadStrategySpecification";
56
57    private static final Color BG_COLOR_ERROR = new Color(255, 224, 224);
58
59    private transient Map<UploadStrategy, JRadioButton> rbStrategy;
60    private transient Map<UploadStrategy, JLabel> lblNumRequests;
61    private transient Map<UploadStrategy, JMultilineLabel> lblStrategies;
62    private final JosmTextField tfChunkSize = new JosmTextField(4);
63    private final JPanel pnlMultiChangesetPolicyPanel = new JPanel(new GridBagLayout());
64    private final JRadioButton rbFillOneChangeset = new JRadioButton(
65            tr("Fill up one changeset and return to the Upload Dialog"));
66    private final JRadioButton rbUseMultipleChangesets = new JRadioButton(
67            tr("Open and use as many new changesets as necessary"));
68    private JMultilineLabel lblMultiChangesetPoliciesHeader;
69
70    private long numUploadedObjects;
71
72    /**
73     * Constructs a new {@code UploadStrategySelectionPanel}.
74     */
75    public UploadStrategySelectionPanel() {
76        build();
77    }
78
79    protected JPanel buildUploadStrategyPanel() {
80        JPanel pnl = new JPanel(new GridBagLayout());
81        ButtonGroup bgStrategies = new ButtonGroup();
82        rbStrategy = new EnumMap<>(UploadStrategy.class);
83        lblStrategies = new EnumMap<>(UploadStrategy.class);
84        lblNumRequests = new EnumMap<>(UploadStrategy.class);
85        for (UploadStrategy strategy: UploadStrategy.values()) {
86            rbStrategy.put(strategy, new JRadioButton());
87            lblNumRequests.put(strategy, new JLabel());
88            lblStrategies.put(strategy, new JMultilineLabel(""));
89            bgStrategies.add(rbStrategy.get(strategy));
90        }
91
92        // -- headline
93        GridBagConstraints gc = new GridBagConstraints();
94        gc.gridx = 0;
95        gc.gridy = 0;
96        gc.weightx = 1.0;
97        gc.weighty = 0.0;
98        gc.gridwidth = 4;
99        gc.fill = GridBagConstraints.HORIZONTAL;
100        gc.insets = new Insets(0, 0, 3, 0);
101        gc.anchor = GridBagConstraints.FIRST_LINE_START;
102        pnl.add(new JMultilineLabel(tr("Please select the upload strategy:")), gc);
103
104        // -- single request strategy
105        gc.gridx = 0;
106        gc.gridy = 1;
107        gc.weightx = 0.0;
108        gc.weighty = 0.0;
109        gc.gridwidth = 1;
110        gc.anchor = GridBagConstraints.FIRST_LINE_START;
111        pnl.add(rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY), gc);
112        gc.gridx = 1;
113        gc.gridy = 1;
114        gc.weightx = 1.0;
115        gc.weighty = 0.0;
116        gc.gridwidth = 2;
117        JMultilineLabel lbl = lblStrategies.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
118        lbl.setText(tr("Upload data in one request"));
119        pnl.add(lbl, gc);
120        gc.gridx = 3;
121        gc.gridy = 1;
122        gc.weightx = 0.0;
123        gc.weighty = 0.0;
124        gc.gridwidth = 1;
125        pnl.add(lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY), gc);
126
127        // -- chunked dataset strategy
128        gc.gridx = 0;
129        gc.gridy = 2;
130        gc.weightx = 0.0;
131        gc.weighty = 0.0;
132        pnl.add(rbStrategy.get(UploadStrategy.CHUNKED_DATASET_STRATEGY), gc);
133        gc.gridx = 1;
134        gc.gridy = 2;
135        gc.weightx = 1.0;
136        gc.weighty = 0.0;
137        gc.gridwidth = 1;
138        lbl = lblStrategies.get(UploadStrategy.CHUNKED_DATASET_STRATEGY);
139        lbl.setText(tr("Upload data in chunks of objects. Chunk size: "));
140        pnl.add(lbl, gc);
141        gc.gridx = 2;
142        gc.gridy = 2;
143        gc.weightx = 0.0;
144        gc.weighty = 0.0;
145        gc.gridwidth = 1;
146        pnl.add(tfChunkSize, gc);
147        gc.gridx = 3;
148        gc.gridy = 2;
149        gc.weightx = 0.0;
150        gc.weighty = 0.0;
151        gc.gridwidth = 1;
152        pnl.add(lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY), gc);
153
154        // -- single request strategy
155        gc.gridx = 0;
156        gc.gridy = 3;
157        gc.weightx = 0.0;
158        gc.weighty = 0.0;
159        pnl.add(rbStrategy.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY), gc);
160        gc.gridx = 1;
161        gc.gridy = 3;
162        gc.weightx = 1.0;
163        gc.weighty = 0.0;
164        gc.gridwidth = 2;
165        lbl = lblStrategies.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY);
166        lbl.setText(tr("Upload each object individually"));
167        pnl.add(lbl, gc);
168        gc.gridx = 3;
169        gc.gridy = 3;
170        gc.weightx = 0.0;
171        gc.weighty = 0.0;
172        gc.gridwidth = 1;
173        pnl.add(lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY), gc);
174
175        tfChunkSize.addFocusListener(new TextFieldFocusHandler());
176        tfChunkSize.getDocument().addDocumentListener(new ChunkSizeInputVerifier());
177
178        StrategyChangeListener strategyChangeListener = new StrategyChangeListener();
179        tfChunkSize.addFocusListener(strategyChangeListener);
180        tfChunkSize.addActionListener(strategyChangeListener);
181        for (UploadStrategy strategy: UploadStrategy.values()) {
182            rbStrategy.get(strategy).addItemListener(strategyChangeListener);
183        }
184
185        return pnl;
186    }
187
188    protected JPanel buildMultiChangesetPolicyPanel() {
189        GridBagConstraints gc = new GridBagConstraints();
190        gc.gridx = 0;
191        gc.gridy = 0;
192        gc.fill = GridBagConstraints.HORIZONTAL;
193        gc.anchor = GridBagConstraints.FIRST_LINE_START;
194        gc.weightx = 1.0;
195        lblMultiChangesetPoliciesHeader = new JMultilineLabel(
196                tr("<html>There are <strong>multiple changesets</strong> necessary in order to upload {0} objects. " +
197                   "Which strategy do you want to use?</html>",
198                        numUploadedObjects));
199        pnlMultiChangesetPolicyPanel.add(lblMultiChangesetPoliciesHeader, gc);
200        gc.gridy = 1;
201        pnlMultiChangesetPolicyPanel.add(rbFillOneChangeset, gc);
202        gc.gridy = 2;
203        pnlMultiChangesetPolicyPanel.add(rbUseMultipleChangesets, gc);
204
205        ButtonGroup bgMultiChangesetPolicies = new ButtonGroup();
206        bgMultiChangesetPolicies.add(rbFillOneChangeset);
207        bgMultiChangesetPolicies.add(rbUseMultipleChangesets);
208        return pnlMultiChangesetPolicyPanel;
209    }
210
211    protected void build() {
212        setLayout(new GridBagLayout());
213        GridBagConstraints gc = new GridBagConstraints();
214        gc.gridx = 0;
215        gc.gridy = 0;
216        gc.fill = GridBagConstraints.HORIZONTAL;
217        gc.weightx = 1.0;
218        gc.weighty = 0.0;
219        gc.anchor = GridBagConstraints.NORTHWEST;
220        gc.insets = new Insets(3, 3, 3, 3);
221
222        add(buildUploadStrategyPanel(), gc);
223        gc.gridy = 1;
224        add(buildMultiChangesetPolicyPanel(), gc);
225
226        // consume remaining space
227        gc.gridy = 2;
228        gc.fill = GridBagConstraints.BOTH;
229        gc.weightx = 1.0;
230        gc.weighty = 1.0;
231        add(new JPanel(), gc);
232
233        Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
234        int maxChunkSize = capabilities != null ? capabilities.getMaxChangesetSize() : -1;
235        pnlMultiChangesetPolicyPanel.setVisible(
236                maxChunkSize > 0 && numUploadedObjects > maxChunkSize
237        );
238    }
239
240    /**
241     * Sets the number of uploaded objects to display
242     * @param numUploadedObjects The number of objects
243     */
244    public void setNumUploadedObjects(int numUploadedObjects) {
245        this.numUploadedObjects = Math.max(numUploadedObjects, 0);
246        updateNumRequestsLabels();
247    }
248
249    /**
250     * Fills the inputs using a {@link UploadStrategySpecification}
251     * @param strategy The strategy
252     */
253    public void setUploadStrategySpecification(UploadStrategySpecification strategy) {
254        if (strategy == null)
255            return;
256        rbStrategy.get(strategy.getStrategy()).setSelected(true);
257        tfChunkSize.setEnabled(strategy.getStrategy() == UploadStrategy.CHUNKED_DATASET_STRATEGY);
258        if (strategy.getStrategy().equals(UploadStrategy.CHUNKED_DATASET_STRATEGY)) {
259            if (strategy.getChunkSize() != UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
260                tfChunkSize.setText(Integer.toString(strategy.getChunkSize()));
261            } else {
262                tfChunkSize.setText("1");
263            }
264        }
265    }
266
267    /**
268     * Gets the upload strategy the user chose
269     * @return The strategy
270     */
271    public UploadStrategySpecification getUploadStrategySpecification() {
272        UploadStrategy strategy = getUploadStrategy();
273        UploadStrategySpecification spec = new UploadStrategySpecification();
274        if (strategy != null) {
275            switch(strategy) {
276            case CHUNKED_DATASET_STRATEGY:
277                spec.setStrategy(strategy).setChunkSize(getChunkSize());
278                break;
279            case INDIVIDUAL_OBJECTS_STRATEGY:
280            case SINGLE_REQUEST_STRATEGY:
281            default:
282                spec.setStrategy(strategy);
283                break;
284            }
285        }
286        if (pnlMultiChangesetPolicyPanel.isVisible()) {
287            if (rbFillOneChangeset.isSelected()) {
288                spec.setPolicy(MaxChangesetSizeExceededPolicy.FILL_ONE_CHANGESET_AND_RETURN_TO_UPLOAD_DIALOG);
289            } else if (rbUseMultipleChangesets.isSelected()) {
290                spec.setPolicy(MaxChangesetSizeExceededPolicy.AUTOMATICALLY_OPEN_NEW_CHANGESETS);
291            } else {
292                spec.setPolicy(null); // unknown policy
293            }
294        } else {
295            spec.setPolicy(null);
296        }
297        return spec;
298    }
299
300    protected UploadStrategy getUploadStrategy() {
301        UploadStrategy strategy = null;
302        for (Entry<UploadStrategy, JRadioButton> e : rbStrategy.entrySet()) {
303            if (e.getValue().isSelected()) {
304                strategy = e.getKey();
305                break;
306            }
307        }
308        return strategy;
309    }
310
311    protected int getChunkSize() {
312        try {
313            return Integer.parseInt(tfChunkSize.getText().trim());
314        } catch (NumberFormatException e) {
315            return UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE;
316        }
317    }
318
319    /**
320     * Load the panel contents from preferences
321     */
322    public void initFromPreferences() {
323        UploadStrategy strategy = UploadStrategy.getFromPreferences();
324        rbStrategy.get(strategy).setSelected(true);
325        int chunkSize = Main.pref.getInt("osm-server.upload-strategy.chunk-size", 1);
326        tfChunkSize.setText(Integer.toString(chunkSize));
327        updateNumRequestsLabels();
328    }
329
330    /**
331     * Stores the values that the user has input into the preferences
332     */
333    public void rememberUserInput() {
334        UploadStrategy strategy = getUploadStrategy();
335        UploadStrategy.saveToPreferences(strategy);
336        int chunkSize;
337        try {
338            chunkSize = Integer.parseInt(tfChunkSize.getText().trim());
339            Main.pref.putInt("osm-server.upload-strategy.chunk-size", chunkSize);
340        } catch (NumberFormatException e) {
341            // don't save invalid value to preferences
342            Logging.trace(e);
343        }
344    }
345
346    protected void updateNumRequestsLabels() {
347        int maxChunkSize = OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize();
348        if (maxChunkSize > 0 && numUploadedObjects > maxChunkSize) {
349            rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setEnabled(false);
350            JMultilineLabel lbl = lblStrategies.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
351            lbl.setText(tr("Upload in one request not possible (too many objects to upload)"));
352            lbl.setToolTipText(tr("<html>Cannot upload {0} objects in one request because the<br>"
353                    + "max. changeset size {1} on server ''{2}'' is exceeded.</html>",
354                    numUploadedObjects, maxChunkSize, OsmApi.getOsmApi().getBaseUrl()
355            )
356            );
357            rbStrategy.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setSelected(true);
358            lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setVisible(false);
359
360            lblMultiChangesetPoliciesHeader.setText(
361                    tr("<html>There are <strong>multiple changesets</strong> necessary in order to upload {0} objects. " +
362                       "Which strategy do you want to use?</html>",
363                            numUploadedObjects));
364            if (!rbFillOneChangeset.isSelected() && !rbUseMultipleChangesets.isSelected()) {
365                rbUseMultipleChangesets.setSelected(true);
366            }
367            pnlMultiChangesetPolicyPanel.setVisible(true);
368
369        } else {
370            rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setEnabled(true);
371            JMultilineLabel lbl = lblStrategies.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
372            lbl.setText(tr("Upload data in one request"));
373            lbl.setToolTipText(null);
374            lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setVisible(true);
375
376            pnlMultiChangesetPolicyPanel.setVisible(false);
377        }
378
379        lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setText(tr("(1 request)"));
380        if (numUploadedObjects == 0) {
381            lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY).setText(tr("(# requests unknown)"));
382            lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
383        } else {
384            lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY).setText(
385                    trn("({0} request)", "({0} requests)", numUploadedObjects, numUploadedObjects)
386            );
387            lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
388            int chunkSize = getChunkSize();
389            if (chunkSize == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
390                lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
391            } else {
392                int chunks = (int) Math.ceil((double) numUploadedObjects / (double) chunkSize);
393                lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(
394                        trn("({0} request)", "({0} requests)", chunks, chunks)
395                );
396            }
397        }
398    }
399
400    /**
401     * Sets the focus on the chunk size field
402     */
403    public void initEditingOfChunkSize() {
404        tfChunkSize.requestFocusInWindow();
405    }
406
407    @Override
408    public void propertyChange(PropertyChangeEvent evt) {
409        if (evt.getPropertyName().equals(UploadedObjectsSummaryPanel.NUM_OBJECTS_TO_UPLOAD_PROP)) {
410            setNumUploadedObjects((Integer) evt.getNewValue());
411        }
412    }
413
414    static class TextFieldFocusHandler extends FocusAdapter {
415        @Override
416        public void focusGained(FocusEvent e) {
417            Component c = e.getComponent();
418            if (c instanceof JosmTextField) {
419                JosmTextField tf = (JosmTextField) c;
420                tf.selectAll();
421            }
422        }
423    }
424
425    class ChunkSizeInputVerifier implements DocumentListener, PropertyChangeListener {
426        protected void setErrorFeedback(JosmTextField tf, String message) {
427            tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
428            tf.setToolTipText(message);
429            tf.setBackground(BG_COLOR_ERROR);
430        }
431
432        protected void clearErrorFeedback(JosmTextField tf, String message) {
433            tf.setBorder(UIManager.getBorder("TextField.border"));
434            tf.setToolTipText(message);
435            tf.setBackground(UIManager.getColor("TextField.background"));
436        }
437
438        protected void validateChunkSize() {
439            try {
440                int chunkSize = Integer.parseInt(tfChunkSize.getText().trim());
441                int maxChunkSize = OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize();
442                if (chunkSize <= 0) {
443                    setErrorFeedback(tfChunkSize, tr("Illegal chunk size <= 0. Please enter an integer > 1"));
444                } else if (maxChunkSize > 0 && chunkSize > maxChunkSize) {
445                    setErrorFeedback(tfChunkSize, tr("Chunk size {0} exceeds max. changeset size {1} for server ''{2}''",
446                            chunkSize, maxChunkSize, OsmApi.getOsmApi().getBaseUrl()));
447                } else {
448                    clearErrorFeedback(tfChunkSize, tr("Please enter an integer > 1"));
449                }
450
451                if (maxChunkSize > 0 && chunkSize > maxChunkSize) {
452                    setErrorFeedback(tfChunkSize, tr("Chunk size {0} exceeds max. changeset size {1} for server ''{2}''",
453                            chunkSize, maxChunkSize, OsmApi.getOsmApi().getBaseUrl()));
454                }
455            } catch (NumberFormatException e) {
456                setErrorFeedback(tfChunkSize, tr("Value ''{0}'' is not a number. Please enter an integer > 1",
457                        tfChunkSize.getText().trim()));
458            } finally {
459                updateNumRequestsLabels();
460            }
461        }
462
463        @Override
464        public void changedUpdate(DocumentEvent arg0) {
465            validateChunkSize();
466        }
467
468        @Override
469        public void insertUpdate(DocumentEvent arg0) {
470            validateChunkSize();
471        }
472
473        @Override
474        public void removeUpdate(DocumentEvent arg0) {
475            validateChunkSize();
476        }
477
478        @Override
479        public void propertyChange(PropertyChangeEvent evt) {
480            if (evt.getSource() == tfChunkSize
481                    && "enabled".equals(evt.getPropertyName())
482                    && (Boolean) evt.getNewValue()
483            ) {
484                validateChunkSize();
485            }
486        }
487    }
488
489    class StrategyChangeListener extends FocusAdapter implements ItemListener, ActionListener {
490
491        protected void notifyStrategy() {
492            firePropertyChange(UPLOAD_STRATEGY_SPECIFICATION_PROP, null, getUploadStrategySpecification());
493        }
494
495        @Override
496        public void itemStateChanged(ItemEvent e) {
497            UploadStrategy strategy = getUploadStrategy();
498            if (strategy == null)
499                return;
500            switch(strategy) {
501            case CHUNKED_DATASET_STRATEGY:
502                tfChunkSize.setEnabled(true);
503                tfChunkSize.requestFocusInWindow();
504                break;
505            default:
506                tfChunkSize.setEnabled(false);
507            }
508            notifyStrategy();
509        }
510
511        @Override
512        public void focusLost(FocusEvent arg0) {
513            notifyStrategy();
514        }
515
516        @Override
517        public void actionPerformed(ActionEvent arg0) {
518            notifyStrategy();
519        }
520    }
521}
Note: See TracBrowser for help on using the repository browser.