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