001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Graphics2D; 007import java.util.Enumeration; 008import java.util.List; 009 010import javax.swing.Action; 011import javax.swing.Icon; 012import javax.swing.tree.DefaultMutableTreeNode; 013import javax.swing.tree.TreeNode; 014 015import org.openstreetmap.josm.Main; 016import org.openstreetmap.josm.actions.RenameLayerAction; 017import org.openstreetmap.josm.data.Bounds; 018import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 019import org.openstreetmap.josm.data.validation.OsmValidator; 020import org.openstreetmap.josm.data.validation.PaintVisitor; 021import org.openstreetmap.josm.data.validation.Severity; 022import org.openstreetmap.josm.data.validation.TestError; 023import org.openstreetmap.josm.gui.MapView; 024import org.openstreetmap.josm.gui.MapView.LayerChangeListener; 025import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 026import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 027import org.openstreetmap.josm.tools.ImageProvider; 028import org.openstreetmap.josm.tools.MultiMap; 029 030/** 031 * A layer showing error messages. 032 * 033 * @author frsantos 034 */ 035public class ValidatorLayer extends Layer implements LayerChangeListener { 036 037 private int updateCount = -1; 038 039 /** 040 * Constructs a new Validator layer 041 */ 042 public ValidatorLayer() { 043 super(tr("Validation errors")); 044 MapView.addLayerChangeListener(this); 045 } 046 047 /** 048 * Return a static icon. 049 */ 050 @Override 051 public Icon getIcon() { 052 return ImageProvider.get("layer", "validator_small"); 053 } 054 055 /** 056 * Draw all primitives in this layer but do not draw modified ones (they 057 * are drawn by the edit layer). 058 * Draw nodes last to overlap the ways they belong to. 059 */ 060 @SuppressWarnings("unchecked") 061 @Override 062 public void paint(final Graphics2D g, final MapView mv, Bounds bounds) { 063 updateCount = Main.map.validatorDialog.tree.getUpdateCount(); 064 DefaultMutableTreeNode root = Main.map.validatorDialog.tree.getRoot(); 065 if (root == null || root.getChildCount() == 0) 066 return; 067 068 PaintVisitor paintVisitor = new PaintVisitor(g, mv); 069 070 DefaultMutableTreeNode severity = (DefaultMutableTreeNode) root.getLastChild(); 071 while (severity != null) { 072 Enumeration<TreeNode> errorMessages = severity.breadthFirstEnumeration(); 073 while (errorMessages.hasMoreElements()) { 074 Object tn = ((DefaultMutableTreeNode) errorMessages.nextElement()).getUserObject(); 075 if (tn instanceof TestError) { 076 paintVisitor.visit((TestError) tn); 077 } 078 } 079 080 // Severities in inverse order 081 severity = severity.getPreviousSibling(); 082 } 083 084 paintVisitor.clearPaintedObjects(); 085 } 086 087 @Override 088 public String getToolTipText() { 089 MultiMap<Severity, TestError> errorTree = new MultiMap<>(); 090 List<TestError> errors = Main.map.validatorDialog.tree.getErrors(); 091 for (TestError e : errors) { 092 errorTree.put(e.getSeverity(), e); 093 } 094 095 StringBuilder b = new StringBuilder(); 096 for (Severity s : Severity.values()) { 097 if (errorTree.containsKey(s)) { 098 b.append(tr(s.toString())).append(": ").append(errorTree.get(s).size()).append("<br>"); 099 } 100 } 101 102 if (b.length() == 0) 103 return "<html>" + tr("No validation errors") + "</html>"; 104 else 105 return "<html>" + tr("Validation errors") + ":<br>" + b + "</html>"; 106 } 107 108 @Override 109 public void mergeFrom(Layer from) { 110 } 111 112 @Override 113 public boolean isMergable(Layer other) { 114 return false; 115 } 116 117 @Override 118 public boolean isChanged() { 119 return updateCount != Main.map.validatorDialog.tree.getUpdateCount(); 120 } 121 122 @Override 123 public void visitBoundingBox(BoundingXYVisitor v) { 124 } 125 126 @Override 127 public Object getInfoComponent() { 128 return getToolTipText(); 129 } 130 131 @Override 132 public Action[] getMenuEntries() { 133 return new Action[] { 134 LayerListDialog.getInstance().createShowHideLayerAction(), 135 LayerListDialog.getInstance().createDeleteLayerAction(), 136 SeparatorLayerAction.INSTANCE, 137 new RenameLayerAction(null, this), 138 SeparatorLayerAction.INSTANCE, 139 new LayerListPopup.InfoAction(this) }; 140 } 141 142 @Override 143 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 144 } 145 146 @Override 147 public void layerAdded(Layer newLayer) { 148 } 149 150 /** 151 * If layer is the OSM Data layer, remove all errors 152 */ 153 @Override 154 public void layerRemoved(Layer oldLayer) { 155 if (oldLayer instanceof OsmDataLayer && Main.isDisplayingMapView() && !Main.main.hasEditLayer()) { 156 Main.main.removeLayer(this); 157 } else if (oldLayer == this) { 158 MapView.removeLayerChangeListener(this); 159 OsmValidator.errorLayer = null; 160 } 161 } 162}