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

Last change on this file since 4357 was 4357, checked in by bastiK, 13 years ago

show conflicts in InspectPrimitiveDialog

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