source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java@ 8308

Last change on this file since 8308 was 8308, checked in by Don-vip, 9 years ago

fix potential NPEs and Sonar issues related to serialization

  • Property svn:eol-style set to native
File size: 14.1 KB
RevLine 
[3669]1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.gui.dialogs.validator;
3
[6469]4import static org.openstreetmap.josm.tools.I18n.tr;
5
[5672]6import java.awt.event.KeyListener;
[3669]7import java.awt.event.MouseEvent;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.Enumeration;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.List;
14import java.util.Map;
[5672]15import java.util.Map.Entry;
[3669]16import java.util.Set;
17
18import javax.swing.JTree;
19import javax.swing.ToolTipManager;
20import javax.swing.tree.DefaultMutableTreeNode;
21import javax.swing.tree.DefaultTreeModel;
22import javax.swing.tree.TreePath;
23import javax.swing.tree.TreeSelectionModel;
24
[5672]25import org.openstreetmap.josm.Main;
[5911]26import org.openstreetmap.josm.data.osm.DataSet;
[3669]27import org.openstreetmap.josm.data.osm.OsmPrimitive;
28import org.openstreetmap.josm.data.validation.Severity;
29import org.openstreetmap.josm.data.validation.TestError;
30import org.openstreetmap.josm.data.validation.util.MultipleNameVisitor;
[6529]31import org.openstreetmap.josm.gui.preferences.validator.ValidatorPreference;
[6706]32import org.openstreetmap.josm.gui.util.GuiHelper;
[5911]33import org.openstreetmap.josm.tools.Destroyable;
[3674]34import org.openstreetmap.josm.tools.MultiMap;
[3669]35
36/**
37 * A panel that displays the error tree. The selection manager
38 * respects clicks into the selection list. Ctrl-click will remove entries from
39 * the list while single click will make the clicked entry the only selection.
40 *
41 * @author frsantos
42 */
[5911]43public class ValidatorTreePanel extends JTree implements Destroyable {
[3669]44
[7266]45 private static final class GroupTreeNode extends DefaultMutableTreeNode {
46
47 public GroupTreeNode(Object userObject) {
48 super(userObject);
49 }
50
51 @Override
52 public String toString() {
53 return tr("{0} ({1})", super.toString(), getLeafCount());
54 }
55 }
56
[3669]57 /**
58 * The validation data.
59 */
[3671]60 protected DefaultTreeModel valTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
[3669]61
62 /** The list of errors shown in the tree */
[8308]63 private transient List<TestError> errors = new ArrayList<>();
[3669]64
65 /**
66 * If {@link #filter} is not <code>null</code> only errors are displayed
67 * that refer to one of the primitives in the filter.
68 */
[8308]69 private transient Set<OsmPrimitive> filter = null;
[3669]70
[5891]71 /** a counter to check if tree has been rebuild */
[3669]72 private int updateCount;
73
74 /**
75 * Constructor
76 * @param errors The list of errors
77 */
78 public ValidatorTreePanel(List<TestError> errors) {
79 ToolTipManager.sharedInstance().registerComponent(this);
[3671]80 this.setModel(valTreeModel);
[3669]81 this.setRootVisible(false);
82 this.setShowsRootHandles(true);
83 this.expandRow(0);
84 this.setVisibleRowCount(8);
85 this.setCellRenderer(new ValidatorTreeRenderer());
86 this.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
87 setErrorList(errors);
[5672]88 for (KeyListener keyListener : getKeyListeners()) {
89 // Fix #3596 - Remove default keyListener to avoid conflicts with JOSM commands
[6990]90 if ("javax.swing.plaf.basic.BasicTreeUI$Handler".equals(keyListener.getClass().getName())) {
[5672]91 removeKeyListener(keyListener);
92 }
93 }
[3669]94 }
95
96 @Override
97 public String getToolTipText(MouseEvent e) {
98 String res = null;
99 TreePath path = getPathForLocation(e.getX(), e.getY());
100 if (path != null) {
101 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
102 Object nodeInfo = node.getUserObject();
103
104 if (nodeInfo instanceof TestError) {
105 TestError error = (TestError) nodeInfo;
106 MultipleNameVisitor v = new MultipleNameVisitor();
107 v.visit(error.getPrimitives());
108 res = "<html>" + v.getText() + "<br>" + error.getMessage();
109 String d = error.getDescription();
110 if (d != null)
111 res += "<br>" + d;
112 res += "</html>";
[3671]113 } else {
[3669]114 res = node.toString();
[3671]115 }
[3669]116 }
117 return res;
118 }
119
120 /** Constructor */
121 public ValidatorTreePanel() {
122 this(null);
123 }
124
125 @Override
126 public void setVisible(boolean v) {
[3671]127 if (v) {
[3669]128 buildTree();
[3671]129 } else {
130 valTreeModel.setRoot(new DefaultMutableTreeNode());
131 }
[3669]132 super.setVisible(v);
133 }
134
135 /**
136 * Builds the errors tree
137 */
138 public void buildTree() {
139 updateCount++;
[6706]140 final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
[3669]141
142 if (errors == null || errors.isEmpty()) {
[6706]143 GuiHelper.runInEDTAndWait(new Runnable() {
144 @Override
145 public void run() {
146 valTreeModel.setRoot(rootNode);
147 }
148 });
[3669]149 return;
150 }
[6468]151 // Sort validation errors - #8517
152 Collections.sort(errors);
[3669]153
154 // Remember the currently expanded rows
[7005]155 Set<Object> oldSelectedRows = new HashSet<>();
[3669]156 Enumeration<TreePath> expanded = getExpandedDescendants(new TreePath(getRoot()));
157 if (expanded != null) {
158 while (expanded.hasMoreElements()) {
159 TreePath path = expanded.nextElement();
160 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
161 Object userObject = node.getUserObject();
[3671]162 if (userObject instanceof Severity) {
[3669]163 oldSelectedRows.add(userObject);
[3671]164 } else if (userObject instanceof String) {
[3669]165 String msg = (String) userObject;
[7270]166 int index = msg.lastIndexOf(" (");
167 if (index > 0) {
168 msg = msg.substring(0, index);
169 }
[3669]170 oldSelectedRows.add(msg);
171 }
172 }
173 }
174
[7005]175 Map<Severity, MultiMap<String, TestError>> errorTree = new HashMap<>();
176 Map<Severity, HashMap<String, MultiMap<String, TestError>>> errorTreeDeep = new HashMap<>();
[3669]177 for (Severity s : Severity.values()) {
[3674]178 errorTree.put(s, new MultiMap<String, TestError>(20));
179 errorTreeDeep.put(s, new HashMap<String, MultiMap<String, TestError>>());
[3669]180 }
181
[6636]182 final Boolean other = ValidatorPreference.PREF_OTHER.get();
[3669]183 for (TestError e : errors) {
[3671]184 if (e.getIgnored()) {
[3669]185 continue;
[3671]186 }
[3669]187 Severity s = e.getSeverity();
[3723]188 if(!other && s == Severity.OTHER) {
189 continue;
190 }
[3669]191 String d = e.getDescription();
192 String m = e.getMessage();
193 if (filter != null) {
194 boolean found = false;
195 for (OsmPrimitive p : e.getPrimitives()) {
196 if (filter.contains(p)) {
197 found = true;
198 break;
199 }
200 }
[3671]201 if (!found) {
[3669]202 continue;
[3671]203 }
[3669]204 }
205 if (d != null) {
[3674]206 MultiMap<String, TestError> b = errorTreeDeep.get(s).get(m);
[3669]207 if (b == null) {
[7005]208 b = new MultiMap<>(20);
[3669]209 errorTreeDeep.get(s).put(m, b);
210 }
[3674]211 b.put(d, e);
[3671]212 } else {
[3674]213 errorTree.get(s).put(m, e);
[3671]214 }
[3669]215 }
216
[7005]217 List<TreePath> expandedPaths = new ArrayList<>();
[3669]218 for (Severity s : Severity.values()) {
[3674]219 MultiMap<String, TestError> severityErrors = errorTree.get(s);
220 Map<String, MultiMap<String, TestError>> severityErrorsDeep = errorTreeDeep.get(s);
[3671]221 if (severityErrors.isEmpty() && severityErrorsDeep.isEmpty()) {
[3669]222 continue;
[3671]223 }
[3669]224
225 // Severity node
[7266]226 DefaultMutableTreeNode severityNode = new GroupTreeNode(s);
[3669]227 rootNode.add(severityNode);
228
[3671]229 if (oldSelectedRows.contains(s)) {
[3669]230 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode }));
[3671]231 }
[3669]232
[5783]233 for (Entry<String, Set<TestError>> msgErrors : severityErrors.entrySet()) {
[3669]234 // Message node
[3674]235 Set<TestError> errs = msgErrors.getValue();
[6469]236 String msg = tr("{0} ({1})", msgErrors.getKey(), errs.size());
[3669]237 DefaultMutableTreeNode messageNode = new DefaultMutableTreeNode(msg);
238 severityNode.add(messageNode);
239
240 if (oldSelectedRows.contains(msgErrors.getKey())) {
241 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, messageNode }));
242 }
243
[3671]244 for (TestError error : errs) {
[3669]245 // Error node
246 DefaultMutableTreeNode errorNode = new DefaultMutableTreeNode(error);
247 messageNode.add(errorNode);
248 }
249 }
[3674]250 for (Entry<String, MultiMap<String, TestError>> bag : severityErrorsDeep.entrySet()) {
[3669]251 // Group node
[3674]252 MultiMap<String, TestError> errorlist = bag.getValue();
[3669]253 DefaultMutableTreeNode groupNode = null;
254 if (errorlist.size() > 1) {
[7266]255 groupNode = new GroupTreeNode(bag.getKey());
[3669]256 severityNode.add(groupNode);
257 if (oldSelectedRows.contains(bag.getKey())) {
258 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, groupNode }));
259 }
260 }
261
[5783]262 for (Entry<String, Set<TestError>> msgErrors : errorlist.entrySet()) {
[3669]263 // Message node
[3674]264 Set<TestError> errs = msgErrors.getValue();
[3669]265 String msg;
[3671]266 if (groupNode != null) {
[6469]267 msg = tr("{0} ({1})", msgErrors.getKey(), errs.size());
[3671]268 } else {
[6469]269 msg = tr("{0} - {1} ({2})", msgErrors.getKey(), bag.getKey(), errs.size());
[3671]270 }
[3669]271 DefaultMutableTreeNode messageNode = new DefaultMutableTreeNode(msg);
[3671]272 if (groupNode != null) {
[3669]273 groupNode.add(messageNode);
[3671]274 } else {
[3669]275 severityNode.add(messageNode);
[3671]276 }
[3669]277
278 if (oldSelectedRows.contains(msgErrors.getKey())) {
279 if (groupNode != null) {
280 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, groupNode,
281 messageNode }));
282 } else {
283 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, messageNode }));
284 }
285 }
286
[3671]287 for (TestError error : errs) {
[3669]288 // Error node
289 DefaultMutableTreeNode errorNode = new DefaultMutableTreeNode(error);
290 messageNode.add(errorNode);
291 }
292 }
293 }
294 }
295
[3671]296 valTreeModel.setRoot(rootNode);
[3669]297 for (TreePath path : expandedPaths) {
298 this.expandPath(path);
299 }
300 }
301
302 /**
303 * Sets the errors list used by a data layer
304 * @param errors The error list that is used by a data layer
305 */
[6890]306 public final void setErrorList(List<TestError> errors) {
[3669]307 this.errors = errors;
[3671]308 if (isVisible()) {
[3669]309 buildTree();
[3671]310 }
[3669]311 }
312
313 /**
314 * Clears the current error list and adds these errors to it
[5891]315 * @param newerrors The validation errors
[3669]316 */
317 public void setErrors(List<TestError> newerrors) {
318 if (errors == null)
319 return;
[5911]320 clearErrors();
321 DataSet ds = Main.main.getCurrentDataSet();
[3669]322 for (TestError error : newerrors) {
[3671]323 if (!error.getIgnored()) {
[3669]324 errors.add(error);
[5911]325 if (ds != null) {
326 ds.addDataSetListener(error);
327 }
[3671]328 }
[3669]329 }
[3671]330 if (isVisible()) {
[3669]331 buildTree();
[3671]332 }
[3669]333 }
334
335 /**
336 * Returns the errors of the tree
[5891]337 * @return the errors of the tree
[3669]338 */
339 public List<TestError> getErrors() {
340 return errors != null ? errors : Collections.<TestError> emptyList();
341 }
342
[5891]343 /**
344 * Returns the filter list
345 * @return the list of primitives used for filtering
346 */
[3669]347 public Set<OsmPrimitive> getFilter() {
348 return filter;
349 }
350
[5891]351 /**
352 * Set the filter list to a set of primitives
353 * @param filter the list of primitives used for filtering
354 */
[3669]355 public void setFilter(Set<OsmPrimitive> filter) {
[3671]356 if (filter != null && filter.isEmpty()) {
[3669]357 this.filter = null;
[3671]358 } else {
[3669]359 this.filter = filter;
[3671]360 }
361 if (isVisible()) {
[3669]362 buildTree();
[3671]363 }
[3669]364 }
365
366 /**
367 * Updates the current errors list
368 */
369 public void resetErrors() {
[7005]370 List<TestError> e = new ArrayList<>(errors);
[3669]371 setErrors(e);
372 }
373
374 /**
[5891]375 * Expands complete tree
[3669]376 */
377 @SuppressWarnings("unchecked")
378 public void expandAll() {
379 DefaultMutableTreeNode root = getRoot();
380
381 int row = 0;
382 Enumeration<DefaultMutableTreeNode> children = root.breadthFirstEnumeration();
383 while (children.hasMoreElements()) {
384 children.nextElement();
385 expandRow(row++);
386 }
387 }
388
389 /**
390 * Returns the root node model.
391 * @return The root node model
392 */
393 public DefaultMutableTreeNode getRoot() {
[3671]394 return (DefaultMutableTreeNode) valTreeModel.getRoot();
[3669]395 }
396
[5891]397 /**
398 * Returns a value to check if tree has been rebuild
399 * @return the current counter
400 */
[3669]401 public int getUpdateCount() {
402 return updateCount;
403 }
[6070]404
[5911]405 private void clearErrors() {
406 if (errors != null) {
407 DataSet ds = Main.main.getCurrentDataSet();
408 if (ds != null) {
409 for (TestError e : errors) {
410 ds.removeDataSetListener(e);
411 }
412 }
413 errors.clear();
414 }
415 }
416
417 @Override
418 public void destroy() {
419 clearErrors();
420 }
[3669]421}
Note: See TracBrowser for help on using the repository browser.