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

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

fix copyright/license headers globally

  • Property svn:eol-style set to native
File size: 14.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.validator;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.KeyListener;
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;
15import java.util.Map.Entry;
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
25import org.openstreetmap.josm.Main;
26import org.openstreetmap.josm.data.osm.DataSet;
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;
31import org.openstreetmap.josm.gui.preferences.validator.ValidatorPreference;
32import org.openstreetmap.josm.gui.util.GuiHelper;
33import org.openstreetmap.josm.tools.Destroyable;
34import org.openstreetmap.josm.tools.MultiMap;
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 */
43public class ValidatorTreePanel extends JTree implements Destroyable {
44
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
57 /**
58 * The validation data.
59 */
60 protected DefaultTreeModel valTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
61
62 /** The list of errors shown in the tree */
63 private transient List<TestError> errors = new ArrayList<>();
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 */
69 private transient Set<OsmPrimitive> filter = null;
70
71 /** a counter to check if tree has been rebuild */
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);
80 this.setModel(valTreeModel);
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);
88 for (KeyListener keyListener : getKeyListeners()) {
89 // Fix #3596 - Remove default keyListener to avoid conflicts with JOSM commands
90 if ("javax.swing.plaf.basic.BasicTreeUI$Handler".equals(keyListener.getClass().getName())) {
91 removeKeyListener(keyListener);
92 }
93 }
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>";
113 } else {
114 res = node.toString();
115 }
116 }
117 return res;
118 }
119
120 /** Constructor */
121 public ValidatorTreePanel() {
122 this(null);
123 }
124
125 @Override
126 public void setVisible(boolean v) {
127 if (v) {
128 buildTree();
129 } else {
130 valTreeModel.setRoot(new DefaultMutableTreeNode());
131 }
132 super.setVisible(v);
133 }
134
135 /**
136 * Builds the errors tree
137 */
138 public void buildTree() {
139 updateCount++;
140 final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
141
142 if (errors == null || errors.isEmpty()) {
143 GuiHelper.runInEDTAndWait(new Runnable() {
144 @Override
145 public void run() {
146 valTreeModel.setRoot(rootNode);
147 }
148 });
149 return;
150 }
151 // Sort validation errors - #8517
152 Collections.sort(errors);
153
154 // Remember the currently expanded rows
155 Set<Object> oldSelectedRows = new HashSet<>();
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();
162 if (userObject instanceof Severity) {
163 oldSelectedRows.add(userObject);
164 } else if (userObject instanceof String) {
165 String msg = (String) userObject;
166 int index = msg.lastIndexOf(" (");
167 if (index > 0) {
168 msg = msg.substring(0, index);
169 }
170 oldSelectedRows.add(msg);
171 }
172 }
173 }
174
175 Map<Severity, MultiMap<String, TestError>> errorTree = new HashMap<>();
176 Map<Severity, HashMap<String, MultiMap<String, TestError>>> errorTreeDeep = new HashMap<>();
177 for (Severity s : Severity.values()) {
178 errorTree.put(s, new MultiMap<String, TestError>(20));
179 errorTreeDeep.put(s, new HashMap<String, MultiMap<String, TestError>>());
180 }
181
182 final Boolean other = ValidatorPreference.PREF_OTHER.get();
183 for (TestError e : errors) {
184 if (e.getIgnored()) {
185 continue;
186 }
187 Severity s = e.getSeverity();
188 if(!other && s == Severity.OTHER) {
189 continue;
190 }
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 }
201 if (!found) {
202 continue;
203 }
204 }
205 if (d != null) {
206 MultiMap<String, TestError> b = errorTreeDeep.get(s).get(m);
207 if (b == null) {
208 b = new MultiMap<>(20);
209 errorTreeDeep.get(s).put(m, b);
210 }
211 b.put(d, e);
212 } else {
213 errorTree.get(s).put(m, e);
214 }
215 }
216
217 List<TreePath> expandedPaths = new ArrayList<>();
218 for (Severity s : Severity.values()) {
219 MultiMap<String, TestError> severityErrors = errorTree.get(s);
220 Map<String, MultiMap<String, TestError>> severityErrorsDeep = errorTreeDeep.get(s);
221 if (severityErrors.isEmpty() && severityErrorsDeep.isEmpty()) {
222 continue;
223 }
224
225 // Severity node
226 DefaultMutableTreeNode severityNode = new GroupTreeNode(s);
227 rootNode.add(severityNode);
228
229 if (oldSelectedRows.contains(s)) {
230 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode }));
231 }
232
233 for (Entry<String, Set<TestError>> msgErrors : severityErrors.entrySet()) {
234 // Message node
235 Set<TestError> errs = msgErrors.getValue();
236 String msg = tr("{0} ({1})", msgErrors.getKey(), errs.size());
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
244 for (TestError error : errs) {
245 // Error node
246 DefaultMutableTreeNode errorNode = new DefaultMutableTreeNode(error);
247 messageNode.add(errorNode);
248 }
249 }
250 for (Entry<String, MultiMap<String, TestError>> bag : severityErrorsDeep.entrySet()) {
251 // Group node
252 MultiMap<String, TestError> errorlist = bag.getValue();
253 DefaultMutableTreeNode groupNode = null;
254 if (errorlist.size() > 1) {
255 groupNode = new GroupTreeNode(bag.getKey());
256 severityNode.add(groupNode);
257 if (oldSelectedRows.contains(bag.getKey())) {
258 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, groupNode }));
259 }
260 }
261
262 for (Entry<String, Set<TestError>> msgErrors : errorlist.entrySet()) {
263 // Message node
264 Set<TestError> errs = msgErrors.getValue();
265 String msg;
266 if (groupNode != null) {
267 msg = tr("{0} ({1})", msgErrors.getKey(), errs.size());
268 } else {
269 msg = tr("{0} - {1} ({2})", msgErrors.getKey(), bag.getKey(), errs.size());
270 }
271 DefaultMutableTreeNode messageNode = new DefaultMutableTreeNode(msg);
272 if (groupNode != null) {
273 groupNode.add(messageNode);
274 } else {
275 severityNode.add(messageNode);
276 }
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
287 for (TestError error : errs) {
288 // Error node
289 DefaultMutableTreeNode errorNode = new DefaultMutableTreeNode(error);
290 messageNode.add(errorNode);
291 }
292 }
293 }
294 }
295
296 valTreeModel.setRoot(rootNode);
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 */
306 public final void setErrorList(List<TestError> errors) {
307 this.errors = errors;
308 if (isVisible()) {
309 buildTree();
310 }
311 }
312
313 /**
314 * Clears the current error list and adds these errors to it
315 * @param newerrors The validation errors
316 */
317 public void setErrors(List<TestError> newerrors) {
318 if (errors == null)
319 return;
320 clearErrors();
321 DataSet ds = Main.main.getCurrentDataSet();
322 for (TestError error : newerrors) {
323 if (!error.getIgnored()) {
324 errors.add(error);
325 if (ds != null) {
326 ds.addDataSetListener(error);
327 }
328 }
329 }
330 if (isVisible()) {
331 buildTree();
332 }
333 }
334
335 /**
336 * Returns the errors of the tree
337 * @return the errors of the tree
338 */
339 public List<TestError> getErrors() {
340 return errors != null ? errors : Collections.<TestError> emptyList();
341 }
342
343 /**
344 * Returns the filter list
345 * @return the list of primitives used for filtering
346 */
347 public Set<OsmPrimitive> getFilter() {
348 return filter;
349 }
350
351 /**
352 * Set the filter list to a set of primitives
353 * @param filter the list of primitives used for filtering
354 */
355 public void setFilter(Set<OsmPrimitive> filter) {
356 if (filter != null && filter.isEmpty()) {
357 this.filter = null;
358 } else {
359 this.filter = filter;
360 }
361 if (isVisible()) {
362 buildTree();
363 }
364 }
365
366 /**
367 * Updates the current errors list
368 */
369 public void resetErrors() {
370 List<TestError> e = new ArrayList<>(errors);
371 setErrors(e);
372 }
373
374 /**
375 * Expands complete tree
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() {
394 return (DefaultMutableTreeNode) valTreeModel.getRoot();
395 }
396
397 /**
398 * Returns a value to check if tree has been rebuild
399 * @return the current counter
400 */
401 public int getUpdateCount() {
402 return updateCount;
403 }
404
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 }
421}
Note: See TracBrowser for help on using the repository browser.