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}