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

Last change on this file since 18069 was 17455, checked in by GerdP, 3 years ago

see #17184: Memory leaks

  • don't keep ref to primitives, it isn't needed anywhere
  • Property svn:eol-style set to native
File size: 9.2 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.GridBagLayout;
9import java.io.PrintWriter;
10import java.io.StringWriter;
11import java.text.Collator;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.List;
15import java.util.Locale;
16import java.util.Map;
17import java.util.Map.Entry;
18import java.util.Objects;
19import java.util.TreeMap;
20import java.util.stream.Collectors;
21
22import javax.swing.JPanel;
23import javax.swing.JScrollPane;
24import javax.swing.JTabbedPane;
25import javax.swing.SingleSelectionModel;
26
27import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
28import org.openstreetmap.josm.data.osm.IPrimitive;
29import org.openstreetmap.josm.data.osm.OsmData;
30import org.openstreetmap.josm.data.osm.PrimitiveComparator;
31import org.openstreetmap.josm.data.osm.User;
32import org.openstreetmap.josm.gui.ExtendedDialog;
33import org.openstreetmap.josm.gui.MainApplication;
34import org.openstreetmap.josm.gui.NavigatableComponent;
35import org.openstreetmap.josm.gui.mappaint.Cascade;
36import org.openstreetmap.josm.gui.mappaint.ElemStyles;
37import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
38import org.openstreetmap.josm.gui.mappaint.MultiCascade;
39import org.openstreetmap.josm.gui.mappaint.StyleCache;
40import org.openstreetmap.josm.gui.mappaint.StyleElementList;
41import org.openstreetmap.josm.gui.mappaint.StyleSource;
42import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
43import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
44import org.openstreetmap.josm.gui.util.GuiHelper;
45import org.openstreetmap.josm.gui.util.WindowGeometry;
46import org.openstreetmap.josm.gui.widgets.JosmTextArea;
47import org.openstreetmap.josm.tools.GBC;
48
49/**
50 * Panel to inspect one or more OsmPrimitives.
51 *
52 * Gives an unfiltered view of the object's internal state.
53 * Might be useful for power users to give more detailed bug reports and
54 * to better understand the JOSM data representation.
55 */
56public class InspectPrimitiveDialog extends ExtendedDialog {
57
58 private boolean mappaintTabLoaded;
59 private boolean editcountTabLoaded;
60
61 /**
62 * Constructs a new {@code InspectPrimitiveDialog}.
63 * @param primitives collection of primitives
64 * @param data data set
65 * @since 12672 (signature)
66 */
67 public InspectPrimitiveDialog(final Collection<? extends IPrimitive> primitives, OsmData<?, ?, ?, ?> data) {
68 super(MainApplication.getMainFrame(), tr("Advanced object info"), tr("Close"));
69 setRememberWindowGeometry(getClass().getName() + ".geometry",
70 WindowGeometry.centerInWindow(MainApplication.getMainFrame(), new Dimension(750, 550)));
71
72 setButtonIcons("ok");
73 final JTabbedPane tabs = new JTabbedPane();
74
75 tabs.addTab(tr("data"), genericMonospacePanel(new JPanel(), buildDataText(data, new ArrayList<>(primitives))));
76
77 final JPanel pMapPaint = new JPanel();
78 tabs.addTab(tr("map style"), pMapPaint);
79 tabs.getModel().addChangeListener(e -> {
80 if (!mappaintTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) {
81 mappaintTabLoaded = true;
82 genericMonospacePanel(pMapPaint, buildMapPaintText());
83 }
84 });
85
86 final JPanel pEditCounts = new JPanel();
87 tabs.addTab(tr("edit counts"), pEditCounts);
88 tabs.getModel().addChangeListener(e -> {
89 if (!editcountTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 2) {
90 editcountTabLoaded = true;
91 genericMonospacePanel(pEditCounts, buildListOfEditorsText(primitives));
92 }
93 });
94
95 setContent(tabs, false);
96 configureContextsensitiveHelp("/Action/InfoAboutElements", true /* show help button */);
97 }
98
99 protected static JPanel genericMonospacePanel(JPanel p, String s) {
100 p.setLayout(new GridBagLayout());
101 JosmTextArea jte = new JosmTextArea();
102 jte.setFont(GuiHelper.getMonospacedFont(jte));
103 jte.setEditable(false);
104 jte.append(s);
105 jte.setCaretPosition(0);
106 p.add(new JScrollPane(jte), GBC.std().fill());
107 return p;
108 }
109
110 protected static String buildDataText(OsmData<?, ?, ?, ?> data, List<IPrimitive> primitives) {
111 InspectPrimitiveDataText dt = new InspectPrimitiveDataText(data);
112 primitives.stream()
113 .sorted(PrimitiveComparator.orderingWaysRelationsNodes().thenComparing(PrimitiveComparator.comparingNames()))
114 .forEachOrdered(dt::addPrimitive);
115 return dt.toString();
116 }
117
118 protected static String buildMapPaintText() {
119 final Collection<? extends IPrimitive> sel = MainApplication.getLayerManager().getActiveData().getAllSelected();
120 ElemStyles elemstyles = MapPaintStyles.getStyles();
121 NavigatableComponent nc = MainApplication.getMap().mapView;
122 double scale = nc.getDist100Pixel();
123
124 final StringWriter stringWriter = new StringWriter();
125 final PrintWriter txtMappaint = new PrintWriter(stringWriter);
126 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().lock();
127 try {
128 for (IPrimitive osm : sel) {
129 String heading = tr("Styles for \"{0}\":", osm.getDisplayName(DefaultNameFormatter.getInstance()));
130 txtMappaint.println(heading);
131 txtMappaint.println(repeatString("=", heading.length()));
132
133 MultiCascade mc = new MultiCascade();
134
135 for (StyleSource s : elemstyles.getStyleSources()) {
136 if (s.active) {
137 heading = tr("{0} style \"{1}\"", getSort(s), s.getDisplayString());
138 txtMappaint.println(heading);
139 txtMappaint.println(repeatString("-", heading.length()));
140 s.apply(mc, osm, scale, false);
141 txtMappaint.println(tr("Display range: {0}", mc.range));
142 for (Entry<String, Cascade> e : mc.getLayers()) {
143 txtMappaint.println(tr("Layer {0}", e.getKey()));
144 txtMappaint.print(" * ");
145 txtMappaint.println(e.getValue());
146 }
147 }
148 }
149 txtMappaint.println();
150 heading = tr("List of generated Styles:");
151 txtMappaint.println(heading);
152 txtMappaint.println(repeatString("-", heading.length()));
153 StyleElementList sl = elemstyles.get(osm, scale, nc);
154 for (StyleElement s : sl) {
155 txtMappaint.print(" * ");
156 txtMappaint.println(s);
157 }
158 txtMappaint.println();
159 txtMappaint.println();
160 }
161 } finally {
162 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().unlock();
163 }
164 if (sel.size() == 2) {
165 List<IPrimitive> selList = new ArrayList<>(sel);
166 StyleCache sc1 = selList.get(0).getCachedStyle();
167 StyleCache sc2 = selList.get(1).getCachedStyle();
168 if (sc1 == sc2) {
169 txtMappaint.println(tr("The 2 selected objects have identical style caches."));
170 }
171 if (!sc1.equals(sc2)) {
172 txtMappaint.println(tr("The 2 selected objects have different style caches."));
173 }
174 if (sc1 != sc2 && sc1.equals(sc2)) {
175 txtMappaint.println(tr("Warning: The 2 selected objects have equal, but not identical style caches."));
176 }
177 }
178 return stringWriter.toString();
179 }
180
181 private static String repeatString(String string, int count) {
182 // Java 11: use String.repeat
183 return new String(new char[count]).replace("\0", string);
184 }
185
186 /* Future Ideas:
187 Calculate the most recent edit date from o.getTimestamp().
188 Sort by the count for presentation, so the most active editors are on top.
189 Count only tagged nodes (so empty way nodes don't inflate counts).
190 */
191 protected static String buildListOfEditorsText(Collection<? extends IPrimitive> primitives) {
192 final Map<String, Long> editCountByUser = primitives.stream()
193 .map(IPrimitive::getUser)
194 .filter(Objects::nonNull)
195 .collect(Collectors.groupingBy(
196 User::getName,
197 () -> new TreeMap<>(Collator.getInstance(Locale.getDefault())),
198 Collectors.counting()));
199
200 // Print the count in sorted order
201 final StringBuilder s = new StringBuilder(48)
202 .append(trn("{0} user last edited the selection:", "{0} users last edited the selection:",
203 editCountByUser.size(), editCountByUser.size()))
204 .append("\n\n");
205 editCountByUser.forEach((username, editCount) ->
206 s.append(String.format("%6d %s", editCount, username)).append('\n'));
207 return s.toString();
208 }
209
210 private static String getSort(StyleSource s) {
211 if (s instanceof MapCSSStyleSource) {
212 return "MapCSS";
213 } else {
214 return tr("UNKNOWN");
215 }
216 }
217}
Note: See TracBrowser for help on using the repository browser.