001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions.upload; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Dimension; 007import java.awt.GridBagLayout; 008import java.util.ArrayList; 009import java.util.Collection; 010import java.util.List; 011 012import javax.swing.JPanel; 013import javax.swing.JScrollPane; 014 015import org.openstreetmap.josm.data.APIDataSet; 016import org.openstreetmap.josm.data.osm.OsmPrimitive; 017import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; 018import org.openstreetmap.josm.data.validation.OsmValidator; 019import org.openstreetmap.josm.data.validation.Severity; 020import org.openstreetmap.josm.data.validation.Test; 021import org.openstreetmap.josm.data.validation.TestError; 022import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor; 023import org.openstreetmap.josm.gui.ExtendedDialog; 024import org.openstreetmap.josm.gui.MainApplication; 025import org.openstreetmap.josm.gui.MapFrame; 026import org.openstreetmap.josm.gui.dialogs.validator.ValidatorTreePanel; 027import org.openstreetmap.josm.gui.layer.OsmDataLayer; 028import org.openstreetmap.josm.gui.layer.ValidatorLayer; 029import org.openstreetmap.josm.gui.util.GuiHelper; 030import org.openstreetmap.josm.gui.widgets.HtmlPanel; 031import org.openstreetmap.josm.tools.GBC; 032 033/** 034 * The action that does the validate thing. 035 * <p> 036 * This action iterates through all active tests and give them the data, so that 037 * each one can test it. 038 * 039 * @author frsantos 040 * @since 3669 041 */ 042public class ValidateUploadHook implements UploadHook { 043 044 /** 045 * Validate the modified data before uploading 046 * @param apiDataSet contains primitives to be uploaded 047 * @return true if upload should continue, else false 048 */ 049 @Override 050 public boolean checkUpload(APIDataSet apiDataSet) { 051 052 OsmValidator.initializeTests(); 053 Collection<Test> tests = OsmValidator.getEnabledTests(true); 054 if (tests.isEmpty()) 055 return true; 056 057 AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor(); 058 v.visit(apiDataSet.getPrimitivesToAdd()); 059 Collection<OsmPrimitive> selection = v.visit(apiDataSet.getPrimitivesToUpdate()); 060 061 List<TestError> errors = new ArrayList<>(30); 062 for (Test test : tests) { 063 test.setBeforeUpload(true); 064 test.setPartialSelection(true); 065 test.startTest(null); 066 test.visit(selection); 067 test.endTest(); 068 if (ValidatorPrefHelper.PREF_OTHER.get() && ValidatorPrefHelper.PREF_OTHER_UPLOAD.get()) { 069 errors.addAll(test.getErrors()); 070 } else { 071 for (TestError e : test.getErrors()) { 072 if (e.getSeverity() != Severity.OTHER) { 073 errors.add(e); 074 } 075 } 076 } 077 test.clear(); 078 test.setBeforeUpload(false); 079 } 080 081 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) { 082 errors.forEach(TestError::updateIgnored); 083 } 084 085 OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer(); 086 if (editLayer != null) { 087 editLayer.validationErrors.clear(); 088 editLayer.validationErrors.addAll(errors); 089 } 090 MapFrame map = MainApplication.getMap(); 091 if (map != null) { 092 map.validatorDialog.tree.setErrors(errors); 093 } 094 if (errors.stream().allMatch(TestError::isIgnored)) 095 return true; 096 097 return displayErrorScreen(errors); 098 } 099 100 /** 101 * Displays a screen where the actions that would be taken are displayed and 102 * give the user the possibility to cancel the upload. 103 * @param errors The errors displayed in the screen 104 * @return <code>true</code>, if the upload should continue. <code>false</code> 105 * if the user requested cancel. 106 */ 107 private static boolean displayErrorScreen(List<TestError> errors) { 108 JPanel p = new JPanel(new GridBagLayout()); 109 ValidatorTreePanel errorPanel = new ValidatorTreePanel(errors); 110 errorPanel.expandAll(); 111 HtmlPanel pnlMessage = new HtmlPanel(); 112 pnlMessage.setText("<html><body>" 113 + tr("The JOSM data validator partially checked the objects to be" 114 + " uploaded and found some problems. Try fixing them, but do not" 115 + " harm valid data. When in doubt ignore the findings.<br>" 116 + " You can see the findings in the Validator Results panel too." 117 + " Further checks on all data can be started from that panel.") 118 + "<table align=\"center\">" 119 + "<tr><td align=\"left\"><b>"+tr("Errors") 120 + " </b></td><td align=\"left\">" 121 + tr("Usually this should be fixed.")+"</td></tr>" 122 + "<tr><td align=\"left\"><b>"+tr("Warnings") 123 + " </b></td><td align=\"left\">" 124 + tr("Fix these when possible.")+"</td></tr>" 125 + "<tr><td align=\"left\"><b>"+tr("Other") 126 + " </b></td><td align=\"left\">" 127 + tr("Informational hints, expect many false entries.")+"</td></tr>" 128 + "</table>" 129 ); 130 pnlMessage.setPreferredSize(new Dimension(500, 150)); 131 p.add(pnlMessage, GBC.eol().fill(GBC.HORIZONTAL)); 132 p.add(new JScrollPane(errorPanel), GBC.eol().fill(GBC.BOTH)); 133 134 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 135 tr("Suspicious data found. Upload anyway?"), 136 tr("Continue upload"), tr("Cancel")) 137 .setButtonIcons("ok", "cancel") 138 .setContent(p); 139 int rc = ed.showDialog().getValue(); 140 GuiHelper.destroyComponents(ed, false); 141 ed.dispose(); 142 if (rc != 1) { 143 OsmValidator.initializeTests(); 144 OsmValidator.initializeErrorLayer(); 145 MainApplication.getMap().validatorDialog.unfurlDialog(); 146 MainApplication.getLayerManager().getLayersOfType(ValidatorLayer.class).forEach(ValidatorLayer::invalidate); 147 return false; 148 } 149 return true; 150 } 151}