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

Last change on this file since 6267 was 6267, checked in by Don-vip, 5 years ago

Sonar/FindBugs - Replace singular fields by local variables

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