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

Last change on this file since 16323 was 16323, checked in by simon04, 4 years ago

Java 8: use Collectors.counting

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