001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.util.Collection;
007import java.util.Collections;
008import java.util.Objects;
009
010import javax.swing.JLabel;
011import javax.swing.text.JTextComponent;
012
013import org.openstreetmap.josm.data.osm.BBox;
014import org.openstreetmap.josm.data.osm.IPrimitive;
015import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
016import org.openstreetmap.josm.spi.preferences.Config;
017import org.openstreetmap.josm.tools.Utils;
018
019/**
020 * Input validators for {@link UploadDialog}
021 */
022abstract class UploadTextComponentValidator extends AbstractTextComponentValidator {
023    private final JLabel feedback;
024    protected boolean uploadRejected;
025
026    UploadTextComponentValidator(JTextComponent tc, JLabel feedback) {
027        super(tc);
028        this.feedback = feedback;
029        this.feedback.setOpaque(true);
030        validate();
031    }
032
033    @Override
034    protected void feedbackValid(String msg) {
035        msg = msg != null ? "<html>\u2714 " + msg : null;
036        super.feedbackValid(msg);
037        if (!Objects.equals(msg, feedback.getText())) {
038            feedback.setText(msg);
039            feedback.setForeground(VALID_COLOR);
040            feedback.setBackground(null);
041            feedback.setBorder(null);
042        }
043    }
044
045    @Override
046    protected void feedbackWarning(String msg) {
047        msg = msg != null ? "<html>" + msg : null;
048        super.feedbackWarning(msg);
049        if (!Objects.equals(msg, feedback.getText())) {
050            feedback.setText(msg);
051            feedback.setForeground(null);
052            feedback.setBackground(WARNING_BACKGROUND);
053            feedback.setBorder(WARNING_BORDER);
054        }
055    }
056
057    @Override
058    public boolean isValid() {
059        throw new UnsupportedOperationException();
060    }
061
062    /**
063     * Determines if the input validator considers the violation bad enough to reject upload.
064     * @return {@code true} if the input validator considers the violation bad enough to reject upload
065     * @since 17238
066     */
067    public final boolean isUploadRejected() {
068        return uploadRejected;
069    }
070
071    /**
072     * Validator for the changeset {@code comment} tag
073     */
074    static class UploadCommentValidator extends UploadTextComponentValidator {
075
076        UploadCommentValidator(JTextComponent tc, JLabel feedback) {
077            super(tc, feedback);
078        }
079
080        @Override
081        public void validate() {
082            if (!Config.getPref().getBoolean("message.upload_comment_is_empty_or_very_short", true)) {
083                feedbackDisabled();
084                return;
085            }
086            String uploadComment = getComponent().getText();
087            if (UploadDialog.UploadAction.isUploadCommentTooShort(uploadComment)) {
088                feedbackWarning(tr("Your upload comment is <i>empty</i>, or <i>very short</i>.<br /><br />" +
089                        "This is technically allowed, but please consider that many users who are<br />" +
090                        "watching changes in their area depend on meaningful changeset comments<br />" +
091                        "to understand what is going on!<br /><br />" +
092                        "If you spend a minute now to explain your change, you will make life<br />" +
093                        "easier for many other mappers.").replace("<br />", " "));
094            } else {
095                String rejection = UploadDialog.UploadAction.validateUploadTag(uploadComment, "upload.comment",
096                        Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
097                uploadRejected = rejection != null;
098                if (uploadRejected) {
099                    feedbackWarning(tr("Your upload comment is <i>rejected</i>.") + "<br />" + rejection);
100                } else {
101                    feedbackValid(tr("Thank you for providing a changeset comment! " +
102                            "This gives other mappers a better understanding of your intent."));
103                }
104            }
105        }
106    }
107
108    /**
109     * Validator for the changeset {@code source} tag
110     */
111    static class UploadSourceValidator extends UploadTextComponentValidator {
112
113        UploadSourceValidator(JTextComponent tc, JLabel feedback) {
114            super(tc, feedback);
115        }
116
117        @Override
118        public void validate() {
119            if (!Config.getPref().getBoolean("message.upload_source_is_empty", true)) {
120                feedbackDisabled();
121                return;
122            }
123            String uploadSource = getComponent().getText();
124            if (Utils.isStripEmpty(uploadSource)) {
125                feedbackWarning(tr("You did not specify a source for your changes.<br />" +
126                        "It is technically allowed, but this information helps<br />" +
127                        "other users to understand the origins of the data.<br /><br />" +
128                        "If you spend a minute now to explain your change, you will make life<br />" +
129                        "easier for many other mappers.").replace("<br />", " "));
130            } else {
131                final String rejection = UploadDialog.UploadAction.validateUploadTag(
132                        uploadSource, "upload.source", Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
133                uploadRejected = rejection != null;
134                if (uploadRejected) {
135                    feedbackWarning(tr("Your changeset source is <i>rejected</i>.") + "<br />" + rejection);
136                } else {
137                    feedbackValid(tr("Thank you for providing the data source!"));
138                }
139            }
140        }
141    }
142
143    /**
144     * Validator for the changeset area
145     */
146    static class UploadAreaValidator extends UploadTextComponentValidator {
147        private double area = Double.NaN;
148
149        UploadAreaValidator(JTextComponent tc, JLabel feedback) {
150            super(tc, feedback);
151        }
152
153        void computeArea(Collection<? extends IPrimitive> primitives) {
154            this.area = primitives.stream()
155                    .map(IPrimitive::getBBox)
156                    .reduce(new BBox(), (b1, b2) -> {
157                        b1.add(b2);
158                        return b1;
159                    }).area();
160            validate();
161        }
162
163        @Override
164        public void validate() {
165            if (Double.isFinite(area) && area <= Config.getPref().getDouble("upload.max-area", 3.)) {
166                feedbackValid(null);
167            } else {
168                feedbackWarning(tr("The bounding box of this changeset is very large – please consider splitting your changes!"));
169            }
170        }
171    }
172}