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    }