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, 4 weeks 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.