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

Last change on this file since 7447 was 7447, checked in by bastiK, 10 years ago

fixed #10425 - ConcurrentModificationException when auto update mappaint file

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