source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java@ 5328

Last change on this file since 5328 was 5328, checked in by bastiK, 12 years ago

fix NPE as deleted nodes can have null coordinates now (see #7847)

  • Property svn:eol-style set to native
File size: 13.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Dimension;
8import java.awt.Font;
9import java.awt.GridBagLayout;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.List;
14import java.util.Map.Entry;
15
16import javax.swing.JPanel;
17import javax.swing.JScrollPane;
18import javax.swing.JTabbedPane;
19import javax.swing.JTextArea;
20import javax.swing.SingleSelectionModel;
21import javax.swing.event.ChangeEvent;
22import javax.swing.event.ChangeListener;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.data.conflict.Conflict;
26import org.openstreetmap.josm.data.osm.Node;
27import org.openstreetmap.josm.data.osm.OsmPrimitive;
28import org.openstreetmap.josm.data.osm.OsmPrimitiveComparator;
29import org.openstreetmap.josm.data.osm.Relation;
30import org.openstreetmap.josm.data.osm.RelationMember;
31import org.openstreetmap.josm.data.osm.Way;
32import org.openstreetmap.josm.gui.DefaultNameFormatter;
33import org.openstreetmap.josm.gui.ExtendedDialog;
34import org.openstreetmap.josm.gui.NavigatableComponent;
35import org.openstreetmap.josm.gui.layer.OsmDataLayer;
36import org.openstreetmap.josm.gui.mappaint.Cascade;
37import org.openstreetmap.josm.gui.mappaint.ElemStyle;
38import org.openstreetmap.josm.gui.mappaint.ElemStyles;
39import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
40import org.openstreetmap.josm.gui.mappaint.MultiCascade;
41import org.openstreetmap.josm.gui.mappaint.StyleCache;
42import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
43import org.openstreetmap.josm.gui.mappaint.StyleSource;
44import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
45import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
46import org.openstreetmap.josm.tools.DateUtils;
47import org.openstreetmap.josm.tools.GBC;
48import org.openstreetmap.josm.tools.WindowGeometry;
49
50/**
51 * Panel to inspect one or more OsmPrimitives.
52 *
53 * Gives an unfiltered view of the object's internal state.
54 * Might be useful for power users to give more detailed bug reports and
55 * to better understand the JOSM data representation.
56 */
57public class InspectPrimitiveDialog extends ExtendedDialog {
58
59 protected List<OsmPrimitive> primitives;
60 protected OsmDataLayer layer;
61 private JTextArea txtData;
62 private JTextArea txtMappaint;
63 boolean mappaintTabLoaded;
64
65 public InspectPrimitiveDialog(Collection<OsmPrimitive> primitives, OsmDataLayer layer) {
66 super(Main.parent, tr("Advanced object info"), new String[] {tr("Close")});
67 this.primitives = new ArrayList<OsmPrimitive>(primitives);
68 this.layer = layer;
69 setRememberWindowGeometry(getClass().getName() + ".geometry",
70 WindowGeometry.centerInWindow(Main.parent, new Dimension(750, 550)));
71
72 setButtonIcons(new String[]{"ok.png"});
73 final JTabbedPane tabs = new JTabbedPane();
74 JPanel pData = buildDataPanel();
75 tabs.addTab(tr("data"), pData);
76 final JPanel pMapPaint = new JPanel();
77 tabs.addTab(tr("map style"), pMapPaint);
78 tabs.getModel().addChangeListener(new ChangeListener() {
79
80 @Override
81 public void stateChanged(ChangeEvent e) {
82 if (!mappaintTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) {
83 mappaintTabLoaded = true;
84 buildMapPaintPanel(pMapPaint);
85 createMapPaintText();
86 }
87 }
88 });
89 setContent(tabs, false);
90 }
91
92 protected JPanel buildDataPanel() {
93 JPanel p = new JPanel(new GridBagLayout());
94 txtData = new JTextArea();
95 txtData.setFont(new Font("Monospaced", txtData.getFont().getStyle(), txtData.getFont().getSize()));
96 txtData.setEditable(false);
97 txtData.setText(buildDataText());
98 txtData.setSelectionStart(0);
99 txtData.setSelectionEnd(0);
100
101 JScrollPane scroll = new JScrollPane(txtData);
102
103 p.add(scroll, GBC.std().fill());
104 return p;
105 }
106
107 protected String buildDataText() {
108 DataText dt = new DataText();
109 Collections.sort(primitives, new OsmPrimitiveComparator());
110 for (OsmPrimitive o : primitives) {
111 dt.addPrimitive(o);
112 }
113 return dt.toString();
114 }
115
116 class DataText {
117 static final String INDENT = " ";
118 static final String NL = "\n";
119
120 private StringBuilder s = new StringBuilder();
121
122 private DataText add(String title, String... values) {
123 s.append(INDENT).append(title);
124 for (String v : values) {
125 s.append(v);
126 }
127 s.append(NL);
128 return this;
129 }
130
131 private String getNameAndId(String name, long id) {
132 if (name != null) {
133 return name + tr(" ({0})", /* sic to avoid thousand seperators */ Long.toString(id));
134 } else {
135 return Long.toString(id);
136 }
137 }
138
139 public void addPrimitive(OsmPrimitive o) {
140
141 addHeadline(o);
142
143 if (!(o.getDataSet() != null && o.getDataSet().getPrimitiveById(o) != null)) {
144 s.append(NL).append(INDENT).append(tr("not in data set"));
145 return;
146 }
147 if (o.isIncomplete()) {
148 s.append(NL).append(INDENT).append(tr("incomplete"));
149 return;
150 }
151 s.append(NL);
152
153 addState(o);
154 addCommon(o);
155 addAttributes(o);
156 addSpecial(o);
157 addReferrers(s, o);
158 addConflicts(o);
159 s.append(NL);
160 }
161
162 void addHeadline(OsmPrimitive o) {
163 addType(o);
164 addNameAndId(o);
165 }
166
167 void addType(OsmPrimitive o) {
168 if (o instanceof Node) {
169 s.append(tr("Node: "));
170 } else if (o instanceof Way) {
171 s.append(tr("Way: "));
172 } else if (o instanceof Relation) {
173 s.append(tr("Relation: "));
174 }
175 }
176
177 void addNameAndId(OsmPrimitive o) {
178 String name = o.get("name");
179 if (name == null) {
180 s.append(o.getUniqueId());
181 } else {
182 s.append(getNameAndId(name, o.getUniqueId()));
183 }
184 }
185
186 void addState(OsmPrimitive o) {
187 StringBuilder sb = new StringBuilder(INDENT);
188 /* selected state is left out: not interesting as it is always selected */
189 if (o.isDeleted()) {
190 sb.append(tr("deleted")).append(INDENT);
191 }
192 if (!o.isVisible()) {
193 sb.append(tr("deleted-on-server")).append(INDENT);
194 }
195 if (o.isModified()) {
196 sb.append(tr("modified")).append(INDENT);
197 }
198 if (o.isDisabledAndHidden()) {
199 sb.append(tr("filtered/hidden")).append(INDENT);
200 }
201 if (o.isDisabled()) {
202 sb.append(tr("filtered/disabled")).append(INDENT);
203 }
204 if (o.hasDirectionKeys()) {
205 if (o.reversedDirection()) {
206 sb.append(tr("has direction keys (reversed)")).append(INDENT);
207 } else {
208 sb.append(tr("has direction keys")).append(INDENT);
209 }
210 }
211 String state = sb.toString().trim();
212 if (!state.isEmpty()) {
213 add(tr("State: "), sb.toString().trim());
214 }
215 }
216
217 void addCommon(OsmPrimitive o) {
218 add(tr("Data Set: "), Integer.toHexString(o.getDataSet().hashCode()));
219 add(tr("Edited at: "), o.isTimestampEmpty() ? tr("<new object>")
220 : DateUtils.fromDate(o.getTimestamp()));
221 add(tr("Edited by: "), o.getUser() == null ? tr("<new object>")
222 : getNameAndId(o.getUser().getName(), o.getUser().getId()));
223 add(tr("Version: "), Integer.toString(o.getVersion()));
224 add(tr("In changeset: "), Integer.toString(o.getChangesetId()));
225 }
226
227 void addAttributes(OsmPrimitive o) {
228 if (o.hasKeys()) {
229 add(tr("Tags: "));
230 for (String key : o.keySet()) {
231 s.append(INDENT).append(INDENT);
232 s.append(String.format("\"%s\"=\"%s\"\n", key, o.get(key)));
233 }
234 }
235 }
236
237 void addSpecial(OsmPrimitive o) {
238 if (o instanceof Node) {
239 addCoordinates((Node) o);
240 } else if (o instanceof Way) {
241 addBbox(o);
242 addWayNodes((Way) o);
243 } else if (o instanceof Relation) {
244 addBbox(o);
245 addRelationMembers((Relation) o);
246 }
247 }
248
249 void addRelationMembers(Relation r) {
250 add(trn("{0} Member: ", "{0} Members: ", r.getMembersCount(), r.getMembersCount()));
251 for (RelationMember m : r.getMembers()) {
252 s.append(INDENT).append(INDENT);
253 addHeadline(m.getMember());
254 s.append(tr(" as \"{0}\"", m.getRole()));
255 s.append(NL);
256 }
257 }
258
259 void addWayNodes(Way w) {
260 add(tr("{0} Nodes: ", w.getNodesCount()));
261 for (Node n : w.getNodes()) {
262 s.append(INDENT).append(INDENT);
263 addNameAndId(n);
264 s.append(NL);
265 }
266 }
267
268 void addBbox(OsmPrimitive o) {
269 if (o.getBBox() != null) {
270 add(tr("Bounding box: "), o.getBBox().toStringCSV(", "));
271 }
272 }
273
274 void addCoordinates(Node n) {
275 if (n.getCoor() != null) {
276 add(tr("Coordinates: "),
277 Double.toString(n.getCoor().lat()), ", ",
278 Double.toString(n.getCoor().lon()));
279 add(tr("Coordinates (projected): "),
280 Double.toString(n.getEastNorth().east()), ", ",
281 Double.toString(n.getEastNorth().north()));
282 }
283 }
284
285 void addReferrers(StringBuilder s, OsmPrimitive o) {
286 List<OsmPrimitive> refs = o.getReferrers();
287 if (!refs.isEmpty()) {
288 add(tr("Part of: "));
289 for (OsmPrimitive p : refs) {
290 s.append(INDENT).append(INDENT);
291 addHeadline(p);
292 s.append(NL);
293 }
294 }
295 }
296
297 void addConflicts(OsmPrimitive o) {
298 Conflict<?> c = layer.getConflicts().getConflictForMy(o);
299 if (c != null) {
300 add(tr("In conflict with: "));
301 addNameAndId(c.getTheir());
302 }
303 }
304
305 @Override
306 public String toString() {
307 return s.toString();
308 }
309 }
310
311 protected void buildMapPaintPanel(JPanel p) {
312 p.setLayout(new GridBagLayout());
313 txtMappaint = new JTextArea();
314 txtMappaint.setFont(new Font("Monospaced", txtMappaint.getFont().getStyle(), txtMappaint.getFont().getSize()));
315 txtMappaint.setEditable(false);
316
317 p.add(new JScrollPane(txtMappaint), GBC.std().fill());
318 }
319
320 protected void createMapPaintText() {
321 final Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
322 ElemStyles elemstyles = MapPaintStyles.getStyles();
323 NavigatableComponent nc = Main.map.mapView;
324 double scale = nc.getDist100Pixel();
325
326 for (OsmPrimitive osm : sel) {
327 txtMappaint.append(tr("Styles Cache for \"{0}\":", osm.getDisplayName(DefaultNameFormatter.getInstance())));
328
329 MultiCascade mc = new MultiCascade();
330
331 for (StyleSource s : elemstyles.getStyleSources()) {
332 if (s.active) {
333 txtMappaint.append(tr("\n\n> applying {0} style \"{1}\"\n", getSort(s), s.getDisplayString()));
334 s.apply(mc, osm, scale, null, false);
335 txtMappaint.append(tr("\nRange:{0}", mc.range));
336 for (Entry<String, Cascade> e : mc.getLayers()) {
337 txtMappaint.append("\n " + e.getKey() + ": \n" + e.getValue());
338 }
339 } else {
340 txtMappaint.append(tr("\n\n> skipping \"{0}\" (not active)", s.getDisplayString()));
341 }
342 }
343 txtMappaint.append(tr("\n\nList of generated Styles:\n"));
344 StyleList sl = elemstyles.get(osm, scale, nc);
345 for (ElemStyle s : sl) {
346 txtMappaint.append(" * " + s + "\n");
347 }
348 txtMappaint.append("\n\n");
349 }
350
351 if (sel.size() == 2) {
352 List<OsmPrimitive> selList = new ArrayList<OsmPrimitive>(sel);
353 StyleCache sc1 = selList.get(0).mappaintStyle;
354 StyleCache sc2 = selList.get(1).mappaintStyle;
355 if (sc1 == sc2) {
356 txtMappaint.append(tr("The 2 selected objects have identical style caches."));
357 }
358 if (!sc1.equals(sc2)) {
359 txtMappaint.append(tr("The 2 selected objects have different style caches."));
360 }
361 if (sc1.equals(sc2) && sc1 != sc2) {
362 txtMappaint.append(tr("Warning: The 2 selected objects have equal, but not identical style caches."));
363 }
364 }
365 }
366
367 private String getSort(StyleSource s) {
368 if (s instanceof XmlStyleSource) {
369 return tr("xml");
370 } else if (s instanceof MapCSSStyleSource) {
371 return tr("mapcss");
372 } else {
373 return tr("unknown");
374 }
375 }
376}
Note: See TracBrowser for help on using the repository browser.