001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trn;
006
007import java.awt.BorderLayout;
008import java.beans.PropertyChangeEvent;
009import java.beans.PropertyChangeListener;
010import java.util.Optional;
011import java.util.stream.Stream;
012
013import javax.swing.BorderFactory;
014import javax.swing.JLabel;
015import javax.swing.JPanel;
016import javax.swing.event.HyperlinkEvent;
017import javax.swing.event.HyperlinkListener;
018
019import org.openstreetmap.josm.data.osm.Changeset;
020import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
021import org.openstreetmap.josm.io.Capabilities;
022import org.openstreetmap.josm.io.OsmApi;
023import org.openstreetmap.josm.io.UploadStrategySpecification;
024import org.openstreetmap.josm.spi.preferences.Config;
025import org.openstreetmap.josm.tools.ImageProvider;
026import org.openstreetmap.josm.tools.StreamUtils;
027import org.openstreetmap.josm.tools.Utils;
028
029/**
030 * A panel that displays a summary of data the user is about to upload
031 * <p>
032 * FIXME this class should extend HtmlPanel instead (duplicated code in here)
033 */
034public class UploadParameterSummaryPanel extends JPanel implements HyperlinkListener, PropertyChangeListener {
035    private transient UploadStrategySpecification spec = new UploadStrategySpecification();
036    private int numObjects;
037    private JMultilineLabel jepMessage;
038    private JLabel lblWarning;
039
040    private transient Changeset selectedChangeset;
041    private boolean closeChangesetAfterNextUpload;
042    private transient Runnable configHandler;
043
044    /**
045     * Constructs a new {@code UploadParameterSummaryPanel}.
046     */
047    public UploadParameterSummaryPanel() {
048        build();
049        updateSummary();
050    }
051
052    protected String buildChangesetSummary() {
053        if (selectedChangeset == null || selectedChangeset.isNew()) {
054            return tr("Objects are uploaded to a <strong>new changeset</strong>.");
055        } else {
056            return tr("Objects are uploaded to the <strong>open changeset</strong> {0} ''{1}''.",
057                    selectedChangeset.getId(),
058                    selectedChangeset.getComment()
059            );
060        }
061    }
062
063    protected String buildChangesetSummary2() {
064        if (closeChangesetAfterNextUpload) {
065            return tr("The changeset is going to be <strong>closed</strong> after this upload");
066        } else {
067            return tr("The changeset is <strong>left open</strong> after this upload");
068        }
069    }
070
071    protected String buildStrategySummary() {
072        if (spec == null)
073            return "";
074        // check whether we can use one changeset only or whether we have to use multiple changesets
075        //
076        boolean useOneChangeset = true;
077        Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
078        int maxChunkSize = capabilities != null ? capabilities.getMaxChangesetSize() : -1;
079        if (maxChunkSize > 0 && numObjects > maxChunkSize) {
080            useOneChangeset = false;
081        }
082
083        int numRequests = spec.getNumRequests(numObjects);
084        if (useOneChangeset) {
085            lblWarning.setVisible(false);
086            if (numRequests == 0) {
087                return trn(
088                        "Uploading <strong>{0} object</strong> to <strong>1 changeset</strong>",
089                        "Uploading <strong>{0} objects</strong> to <strong>1 changeset</strong>",
090                        numObjects, numObjects
091                );
092            } else if (numRequests == 1) {
093                return trn(
094                        "Uploading <strong>{0} object</strong> to <strong>1 changeset</strong> using <strong>1 request</strong>",
095                        "Uploading <strong>{0} objects</strong> to <strong>1 changeset</strong> using <strong>1 request</strong>",
096                        numObjects, numObjects
097                );
098            } else if (numRequests > 1) {
099                return tr("Uploading <strong>{0} objects</strong> to <strong>1 changeset</strong> using <strong>{1} requests</strong>",
100                        numObjects, numRequests);
101            }
102        } else {
103            lblWarning.setVisible(true);
104            if (numRequests == 0) {
105                return tr("{0} objects exceed the max. allowed {1} objects in a changeset on the server ''{2}''. " +
106                        "Please <a href=\"urn:changeset-configuration\">configure</a> how to proceed with <strong>multiple changesets</strong>",
107                        numObjects, maxChunkSize, OsmApi.getOsmApi().getBaseUrl());
108            } else if (numRequests > 1) {
109                return tr("Uploading <strong>{0} objects</strong> to <strong>multiple changesets</strong> using <strong>{1} requests</strong>",
110                        numObjects, numRequests);
111            }
112        }
113        return "";
114    }
115
116    protected void build() {
117        jepMessage = new JMultilineLabel("");
118        jepMessage.addHyperlinkListener(this);
119
120        setLayout(new BorderLayout());
121        setBorder(BorderFactory.createTitledBorder(tr("Settings:")));
122        add(jepMessage, BorderLayout.CENTER);
123        lblWarning = new JLabel("");
124        lblWarning.setVisible(false);
125        lblWarning.setLabelFor(jepMessage);
126        lblWarning.setIcon(ImageProvider.get("warning-small"));
127        lblWarning.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
128        JPanel pnl = new JPanel(new BorderLayout());
129        pnl.add(lblWarning, BorderLayout.NORTH);
130        add(pnl, BorderLayout.WEST);
131    }
132
133    public void setConfigurationParameterRequestListener(Runnable handler) {
134        this.configHandler = handler;
135    }
136
137    /**
138     * Sets the {@link UploadStrategySpecification} the user chose
139     * @param spec The specification to display
140     */
141    public void setUploadStrategySpecification(UploadStrategySpecification spec) {
142        this.spec = spec;
143        updateSummary();
144    }
145
146    /**
147     * Sets the number of objects that will be uploaded
148     * @param numObjects The number to display
149     */
150    public void setNumObjects(int numObjects) {
151        this.numObjects = numObjects;
152        updateSummary();
153    }
154
155    /**
156     * Display that the changeset will be closed after the upload
157     * @param value <code>true</code> if it will be closed
158     */
159    public void setCloseChangesetAfterNextUpload(boolean value) {
160        this.closeChangesetAfterNextUpload = value;
161        updateSummary();
162    }
163
164    protected void updateSummary() {
165        final String server = Optional.of(OsmApi.getOsmApi().getServerUrl())
166                .filter(url -> !Config.getUrls().getDefaultOsmApiUrl().equals(url))
167                .map(url -> tr("… to server: <strong>{0}</strong>", url))
168                .orElse("");
169        final String html = Stream.of(buildChangesetSummary(), buildChangesetSummary2(), buildStrategySummary(), server)
170                .filter(s -> !Utils.isEmpty(s))
171                .collect(StreamUtils.toHtmlList());
172        jepMessage.setText(html);
173        validate();
174    }
175
176    /* --------------------------------------------------------------------- */
177    /* Interface HyperlinkListener
178    /* --------------------------------------------------------------------- */
179    @Override
180    public void hyperlinkUpdate(HyperlinkEvent e) {
181        if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) {
182            String desc = e.getDescription();
183            if (desc == null || configHandler == null)
184                return;
185            if ("urn:changeset-configuration".equals(desc)) {
186                configHandler.run();
187            }
188        }
189    }
190
191    /* --------------------------------------------------------------------- */
192    /* Interface PropertyChangeListener
193    /* --------------------------------------------------------------------- */
194    @Override
195    public void propertyChange(PropertyChangeEvent evt) {
196        if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) {
197            selectedChangeset = (Changeset) evt.getNewValue();
198            updateSummary();
199        } else if (evt.getPropertyName().equals(ChangesetManagementPanel.CLOSE_CHANGESET_AFTER_UPLOAD)) {
200            closeChangesetAfterNextUpload = (Boolean) evt.getNewValue();
201            updateSummary();
202        } else if (evt.getPropertyName().equals(UploadStrategySelectionPanel.UPLOAD_STRATEGY_SPECIFICATION_PROP)) {
203            this.spec = (UploadStrategySpecification) evt.getNewValue();
204            updateSummary();
205        }
206    }
207}