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

Last change on this file since 19050 was 19050, checked in by taylor.smock, 15 months ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

  • Property svn:eol-style set to native
File size: 16.6 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.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.util.EnumMap;
18import java.util.Map;
19import java.util.Map.Entry;
20
21import javax.swing.BorderFactory;
22import javax.swing.ButtonGroup;
23import javax.swing.JLabel;
24import javax.swing.JPanel;
25import javax.swing.JRadioButton;
26import javax.swing.text.JTextComponent;
27
28import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
29import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
30import org.openstreetmap.josm.gui.widgets.JosmTextField;
31import org.openstreetmap.josm.io.Capabilities;
32import org.openstreetmap.josm.io.MaxChangesetSizeExceededPolicy;
33import org.openstreetmap.josm.io.OsmApi;
34import org.openstreetmap.josm.io.UploadStrategy;
35import org.openstreetmap.josm.io.UploadStrategySpecification;
36import org.openstreetmap.josm.spi.preferences.Config;
37import org.openstreetmap.josm.tools.Logging;
38
39/**
40 * UploadStrategySelectionPanel is a panel for selecting an upload strategy.
41 *
42 * Clients can listen for property change events for the property
43 * {@link #UPLOAD_STRATEGY_SPECIFICATION_PROP}.
44 */
45public class UploadStrategySelectionPanel extends JPanel {
46
47 /**
48 * The property for the upload strategy
49 */
50 public static final String UPLOAD_STRATEGY_SPECIFICATION_PROP =
51 UploadStrategySelectionPanel.class.getName() + ".uploadStrategySpecification";
52
53 private transient Map<UploadStrategy, JRadioButton> rbStrategy;
54 private transient Map<UploadStrategy, JLabel> lblNumRequests;
55 private final JosmTextField tfChunkSize = new JosmTextField(4);
56 private final JPanel pnlMultiChangesetPolicyPanel = new JPanel(new GridBagLayout());
57 private final JRadioButton rbFillOneChangeset = new JRadioButton();
58 private final JRadioButton rbUseMultipleChangesets = new JRadioButton();
59 private JMultilineLabel lblMultiChangesetPoliciesHeader;
60
61 private long numUploadedObjects;
62
63 /**
64 * Constructs a new {@code UploadStrategySelectionPanel}.
65 */
66 public UploadStrategySelectionPanel() {
67 build();
68 }
69
70 protected JPanel buildUploadStrategyPanel() {
71 JPanel pnl = new JPanel(new GridBagLayout());
72 pnl.setBorder(BorderFactory.createTitledBorder(tr("Please select the upload strategy:")));
73 ButtonGroup bgStrategies = new ButtonGroup();
74 rbStrategy = new EnumMap<>(UploadStrategy.class);
75 lblNumRequests = new EnumMap<>(UploadStrategy.class);
76 for (UploadStrategy strategy: UploadStrategy.values()) {
77 rbStrategy.put(strategy, new JRadioButton());
78 lblNumRequests.put(strategy, new JLabel());
79 bgStrategies.add(rbStrategy.get(strategy));
80 }
81
82 // -- single request strategy
83 GridBagConstraints gc = new GridBagConstraints();
84 gc.gridx = 0;
85 gc.gridy = 1;
86 gc.weightx = 0.0;
87 gc.weighty = 0.0;
88 gc.gridwidth = 1;
89 gc.fill = GridBagConstraints.HORIZONTAL;
90 gc.insets = new Insets(3, 3, 3, 3);
91 gc.anchor = GridBagConstraints.FIRST_LINE_START;
92 JRadioButton radioButton = rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
93 radioButton.setText(tr("Upload all objects in one request"));
94 pnl.add(radioButton, gc);
95 gc.gridx = 2;
96 gc.weightx = 1.0;
97 pnl.add(lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY), gc);
98
99 // -- chunked dataset strategy
100 gc.gridy++;
101 gc.gridx = 0;
102 gc.weightx = 0.0;
103 radioButton = rbStrategy.get(UploadStrategy.CHUNKED_DATASET_STRATEGY);
104 radioButton.setText(tr("Upload objects in chunks of size: "));
105 pnl.add(radioButton, gc);
106 gc.gridx = 1;
107 pnl.add(tfChunkSize, gc);
108 gc.gridx = 2;
109 pnl.add(lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY), gc);
110
111 // -- single request strategy
112 gc.gridy++;
113 gc.gridx = 0;
114 radioButton = rbStrategy.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY);
115 radioButton.setText(tr("Upload each object individually"));
116 pnl.add(radioButton, gc);
117 gc.gridx = 2;
118 pnl.add(lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY), gc);
119
120 new ChunkSizeValidator(tfChunkSize);
121
122 StrategyChangeListener strategyChangeListener = new StrategyChangeListener();
123 tfChunkSize.addFocusListener(strategyChangeListener);
124 tfChunkSize.addActionListener(strategyChangeListener);
125 for (UploadStrategy strategy: UploadStrategy.values()) {
126 rbStrategy.get(strategy).addItemListener(strategyChangeListener);
127 }
128
129 return pnl;
130 }
131
132 protected JPanel buildMultiChangesetPolicyPanel() {
133 GridBagConstraints gc = new GridBagConstraints();
134 gc.gridx = 0;
135 gc.gridy = 0;
136 gc.fill = GridBagConstraints.HORIZONTAL;
137 gc.anchor = GridBagConstraints.FIRST_LINE_START;
138 gc.insets = new Insets(3, 3, 3, 3);
139 gc.weightx = 1.0;
140 lblMultiChangesetPoliciesHeader = new JMultilineLabel(
141 tr("<html><strong>Multiple changesets</strong> are necessary to upload {0} objects. " +
142 "Please select a strategy:</html>",
143 numUploadedObjects));
144 pnlMultiChangesetPolicyPanel.add(lblMultiChangesetPoliciesHeader, gc);
145 gc.gridy++;
146 rbFillOneChangeset.setText(tr("Fill up one changeset and return to the Upload Dialog"));
147 pnlMultiChangesetPolicyPanel.add(rbFillOneChangeset, gc);
148 gc.gridy++;
149 rbUseMultipleChangesets.setText(tr("Open and use as many new changesets as necessary"));
150 pnlMultiChangesetPolicyPanel.add(rbUseMultipleChangesets, gc);
151
152 ButtonGroup bgMultiChangesetPolicies = new ButtonGroup();
153 bgMultiChangesetPolicies.add(rbFillOneChangeset);
154 bgMultiChangesetPolicies.add(rbUseMultipleChangesets);
155 return pnlMultiChangesetPolicyPanel;
156 }
157
158 protected void build() {
159 setLayout(new GridBagLayout());
160 GridBagConstraints gc = new GridBagConstraints();
161 gc.gridx = 0;
162 gc.gridy = 0;
163 gc.fill = GridBagConstraints.HORIZONTAL;
164 gc.weightx = 1.0;
165 gc.weighty = 0.0;
166 gc.anchor = GridBagConstraints.NORTHWEST;
167
168 add(buildUploadStrategyPanel(), gc);
169 gc.gridy = 1;
170 add(buildMultiChangesetPolicyPanel(), gc);
171
172 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
173 int maxChunkSize = capabilities != null ? capabilities.getMaxChangesetSize() : -1;
174 pnlMultiChangesetPolicyPanel.setVisible(
175 maxChunkSize > 0 && numUploadedObjects > maxChunkSize
176 );
177 }
178
179 /**
180 * Sets the number of uploaded objects to display
181 * @param numUploadedObjects The number of objects
182 */
183 public void setNumUploadedObjects(int numUploadedObjects) {
184 this.numUploadedObjects = Math.max(numUploadedObjects, 0);
185 updateNumRequestsLabels();
186 }
187
188 /**
189 * Fills the inputs using a {@link UploadStrategySpecification}
190 * @param strategy The strategy
191 */
192 public void setUploadStrategySpecification(UploadStrategySpecification strategy) {
193 if (strategy == null)
194 return;
195 rbStrategy.get(strategy.getStrategy()).setSelected(true);
196 tfChunkSize.setEnabled(strategy.getStrategy() == UploadStrategy.CHUNKED_DATASET_STRATEGY);
197 if (strategy.getStrategy() == UploadStrategy.CHUNKED_DATASET_STRATEGY) {
198 if (strategy.getChunkSize() != UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
199 tfChunkSize.setText(Integer.toString(strategy.getChunkSize()));
200 } else {
201 tfChunkSize.setText("1");
202 }
203 }
204 }
205
206 /**
207 * Gets the upload strategy the user chose
208 * @return The strategy
209 */
210 public UploadStrategySpecification getUploadStrategySpecification() {
211 UploadStrategy strategy = getUploadStrategy();
212 UploadStrategySpecification spec = new UploadStrategySpecification();
213 if (strategy != null) {
214 switch (strategy) {
215 case CHUNKED_DATASET_STRATEGY:
216 spec.setStrategy(strategy).setChunkSize(getChunkSize());
217 break;
218 case INDIVIDUAL_OBJECTS_STRATEGY:
219 case SINGLE_REQUEST_STRATEGY:
220 default:
221 spec.setStrategy(strategy);
222 break;
223 }
224 }
225 if (pnlMultiChangesetPolicyPanel.isVisible()) {
226 if (rbFillOneChangeset.isSelected()) {
227 spec.setPolicy(MaxChangesetSizeExceededPolicy.FILL_ONE_CHANGESET_AND_RETURN_TO_UPLOAD_DIALOG);
228 } else if (rbUseMultipleChangesets.isSelected()) {
229 spec.setPolicy(MaxChangesetSizeExceededPolicy.AUTOMATICALLY_OPEN_NEW_CHANGESETS);
230 } else {
231 spec.setPolicy(null); // unknown policy
232 }
233 } else {
234 spec.setPolicy(null);
235 }
236 return spec;
237 }
238
239 protected UploadStrategy getUploadStrategy() {
240 return rbStrategy.entrySet().stream()
241 .filter(e -> e.getValue().isSelected())
242 .findFirst()
243 .map(Entry::getKey)
244 .orElse(null);
245 }
246
247 protected int getChunkSize() {
248 try {
249 return Integer.parseInt(tfChunkSize.getText().trim());
250 } catch (NumberFormatException e) {
251 return UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE;
252 }
253 }
254
255 /**
256 * Load the panel contents from preferences
257 */
258 public void initFromPreferences() {
259 UploadStrategy strategy = UploadStrategy.getFromPreferences();
260 rbStrategy.get(strategy).setSelected(true);
261 int chunkSize = Config.getPref().getInt("osm-server.upload-strategy.chunk-size", 1000);
262 tfChunkSize.setText(Integer.toString(chunkSize));
263 updateNumRequestsLabels();
264 }
265
266 /**
267 * Stores the values that the user has input into the preferences
268 */
269 public void rememberUserInput() {
270 UploadStrategy strategy = getUploadStrategy();
271 UploadStrategy.saveToPreferences(strategy);
272 int chunkSize;
273 try {
274 chunkSize = Integer.parseInt(tfChunkSize.getText().trim());
275 Config.getPref().putInt("osm-server.upload-strategy.chunk-size", chunkSize);
276 } catch (NumberFormatException e) {
277 // don't save invalid value to preferences
278 Logging.trace(e);
279 }
280 }
281
282 protected void updateNumRequestsLabels() {
283 int maxChunkSize = OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize();
284 if (maxChunkSize > 0 && numUploadedObjects > maxChunkSize) {
285 rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setEnabled(false);
286 JRadioButton lbl = rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
287 lbl.setEnabled(false);
288 lbl.setToolTipText(tr("<html>Cannot upload {0} objects in one request because the<br>"
289 + "max. changeset size {1} on server ''{2}'' is exceeded.</html>",
290 numUploadedObjects, maxChunkSize, OsmApi.getOsmApi().getBaseUrl()
291 )
292 );
293 rbStrategy.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setSelected(true);
294 lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setVisible(false);
295
296 lblMultiChangesetPoliciesHeader.setText(
297 tr("<html>There are <strong>multiple changesets</strong> necessary in order to upload {0} objects. " +
298 "Which strategy do you want to use?</html>",
299 numUploadedObjects));
300 if (!rbFillOneChangeset.isSelected() && !rbUseMultipleChangesets.isSelected()) {
301 rbUseMultipleChangesets.setSelected(true);
302 }
303 pnlMultiChangesetPolicyPanel.setVisible(true);
304
305 } else {
306 rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setEnabled(true);
307 JRadioButton lbl = rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY);
308 lbl.setEnabled(true);
309 lbl.setToolTipText(null);
310 lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setVisible(true);
311
312 pnlMultiChangesetPolicyPanel.setVisible(false);
313 }
314
315 lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setText(tr("(1 request)"));
316 if (numUploadedObjects == 0) {
317 lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY).setText(tr("(# requests unknown)"));
318 lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
319 } else {
320 lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY).setText(
321 trn("({0} request)", "({0} requests)", numUploadedObjects, numUploadedObjects)
322 );
323 lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
324 int chunkSize = getChunkSize();
325 if (chunkSize == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
326 lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
327 } else {
328 int chunks = (int) Math.ceil((double) numUploadedObjects / (double) chunkSize);
329 lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(
330 trn("({0} request)", "({0} requests)", chunks, chunks)
331 );
332 }
333 }
334 }
335
336 /**
337 * Sets the focus on the chunk size field
338 */
339 public void initEditingOfChunkSize() {
340 tfChunkSize.requestFocusInWindow();
341 }
342
343 class ChunkSizeValidator extends AbstractTextComponentValidator {
344 ChunkSizeValidator(JTextComponent tc) {
345 super(tc);
346 }
347
348 @Override
349 public void validate() {
350 try {
351 int chunkSize = Integer.parseInt(tfChunkSize.getText().trim());
352 int maxChunkSize = OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize();
353 if (chunkSize <= 0) {
354 feedbackInvalid(tr("Illegal chunk size <= 0. Please enter an integer > 1"));
355 } else if (maxChunkSize > 0 && chunkSize > maxChunkSize) {
356 feedbackInvalid(tr("Chunk size {0} exceeds max. changeset size {1} for server ''{2}''",
357 chunkSize, maxChunkSize, OsmApi.getOsmApi().getBaseUrl()));
358 } else {
359 feedbackValid(null);
360 }
361
362 if (maxChunkSize > 0 && chunkSize > maxChunkSize) {
363 feedbackInvalid(tr("Chunk size {0} exceeds max. changeset size {1} for server ''{2}''",
364 chunkSize, maxChunkSize, OsmApi.getOsmApi().getBaseUrl()));
365 }
366 } catch (NumberFormatException e) {
367 feedbackInvalid(tr("Value ''{0}'' is not a number. Please enter an integer > 1",
368 tfChunkSize.getText().trim()));
369 } finally {
370 updateNumRequestsLabels();
371 }
372 }
373
374 @Override
375 public boolean isValid() {
376 throw new UnsupportedOperationException();
377 }
378 }
379
380 class StrategyChangeListener implements FocusListener, ItemListener, ActionListener {
381
382 protected void notifyStrategy() {
383 firePropertyChange(UPLOAD_STRATEGY_SPECIFICATION_PROP, null, getUploadStrategySpecification());
384 }
385
386 @Override
387 public void itemStateChanged(ItemEvent e) {
388 UploadStrategy strategy = getUploadStrategy();
389 if (strategy == null)
390 return;
391 switch (strategy) {
392 case CHUNKED_DATASET_STRATEGY:
393 tfChunkSize.setEnabled(true);
394 tfChunkSize.requestFocusInWindow();
395 break;
396 default:
397 tfChunkSize.setEnabled(false);
398 }
399 notifyStrategy();
400 }
401
402 @Override
403 public void focusGained(FocusEvent e) {
404 Component c = e.getComponent();
405 if (c instanceof JosmTextField) {
406 JosmTextField tf = (JosmTextField) c;
407 tf.selectAll();
408 }
409 }
410
411 @Override
412 public void focusLost(FocusEvent e) {
413 notifyStrategy();
414 }
415
416 @Override
417 public void actionPerformed(ActionEvent e) {
418 notifyStrategy();
419 }
420 }
421}
Note: See TracBrowser for help on using the repository browser.