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