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 }